题目
https://loj.ac/problem/2290
思路
先考虑n!怎么做
若组0、1、2有恰好一条边在这个匹配,则概率∗1/2
若组1有恰好两条边,则概率∗1/2
若组2有恰好两条边,则概率∗0
设一个f[S1][S2]表示左边选了S1,右边选了S2的概率和。
需要确立一个转移顺序,也就是每次转移必须使S1的最低的0变成1.
状态数不是很多
代码
#include<bits/stdc++.h>
#define mod 1000000007
#define Inv2 500000004
#define Inv4 250000002
using namespace std;
template <typename T>
void read(T &s) {
s = 0;
char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = ((s + (s << 2)) << 1) + ch - '0',ch = getchar();
}
int n,m,tl,tw[32];
map<int,int> f;
int Fast_Power(int u,int k) {
int Sum = 1;
while (k) {
if(k & 1)
Sum = 1ll * u * Sum % mod;
u = 1ll * u * u % mod,k >>= 1;
}
return Sum;
}
struct Line {
int Sta,val;
} t[510];
int DFS(int w) {
if(w == tw[2 * n] - 1)
return 1;
if(f.count(w))
return f[w];
// cout << w << " ";
int now = 0,Tmp = 0;
for(int i = 0; i < n; ++i)
if(!(tw[i] & w))
now = tw[i];
for(int i = 1; i <= tl; ++i)
if((now & t[i].Sta) && !(t[i].Sta & w))
Tmp = (Tmp + 1ll * DFS(w | t[i].Sta) * t[i].val) % mod;
return f[w] = Tmp;
}
int main() {
read(n),read(m),tw[0] = 1;
for(int i = 1; i <= 2 * n; ++i) tw[i] = tw[i - 1] << 1;
for(int i = 1,opt,x,y,Tmp; i <= m; ++i) {
read(opt),read(x),read(y),x--,y--;
Tmp = tw[x] + tw[y + n],t[++tl].Sta = Tmp,t[tl].val = Inv2;
if(opt) {
read(x),read(y),x--,y--,t[++tl].Sta = tw[x] + tw[y + n],t[tl].val = Inv2;
if(Tmp & (tw[x] | tw[y + n]))
continue;
Tmp |= tw[x] | tw[y + n];
t[++tl].Sta = Tmp;
if(opt == 2)
t[tl].val = -Inv4 + mod;
else
t[tl].val = Inv4;
}
}
printf("%lld\n",1ll * DFS(0) * Fast_Power(2,n) % mod);
return 0;
}