题解
考虑一个完美匹配出现的概率 $\times 2^n$ 对答案的贡献,初始是 $1$ ,如果有出现一组 $t=2$ 的边的话,那贡献就是 $0$ ,否则每出现一组 $t=1$ 的边就要 $\times 2$ ,所以有个暴力的 $\text{dp}$ : $f[s1][s2]$ 表示左边出现点的状态为 $s1$ ,右边为 $s2$ 的答案,为了避免重复我们每次挑 $s1$ 中编号最小的点进行转移,即 $f[s1][s2]=\sum_{y \in s2}f[s1 \wedge x][s2 \wedge y]$ ,考虑如果 $(x,y)$ 有一组 $t=1$ 的边 $(u,v)$ 也在 $s1,s2$ 集合中的话,那要再加上 $f[s1 \wedge x \wedge u][s2 \wedge y \wedge v]$ ,反之如果有一组 $t=2$ 的在集合中的话就要扣掉,记忆化搜索即可。
代码
#include <bits/stdc++.h> using namespace std; const int P=1e9+7;map<int,int>f; int n,m,k,a[15][15],c[1<<15],b[15][15][2]; int X(int x){return x>=P?x-P:x;} int F(int i,int j){ if (f.count(i<<n|j)) return f[i<<n|j]; int w=0,x=i&-i,u,v; for (int y=j&-j,k=j;k;k^=y,y=k&-k){ if (!~a[c[x]][c[y]]) continue; w=X(w+F(i^x,j^y)); u=b[c[x]][c[y]][0];v=b[c[x]][c[y]][1]; if (a[c[x]][c[y]] && (u&i) && (v&j) && u!=x && v!=y){ if (a[c[x]][c[y]]&1) w=X(w+F(i^x^u,j^y^v)); else w=X(w+P-F(i^x^u,j^y^v)); } } return f[i<<n|j]=w; } int main(){ cin>>n>>m;k=1<<n; for (int i=0;i<n;i++) c[1<<i]=i; memset(a,-1,sizeof a); for (int x,y,u,v,o,i=1;i<=m;i++){ scanf("%d%d%d",&o,&x,&y); x--;y--;a[x][y]=o; if (o){ scanf("%d%d",&u,&v); u--;v--;a[u][v]=o; b[x][y][0]=1<<u;b[x][y][1]=1<<v; b[u][v][0]=1<<x;b[u][v][1]=1<<y; } } f[0]=1; printf("%d\n",F(k-1,k-1)); return 0; }