题意:有一个左右各n个点的二分图,对于连边组有一些性质:1号组的一条边,有50%的概率出现。2号组两条边,有50%的概率同时出现,50%的概率同时不出现。3号组两条边,有50%的概率出现第一条,有50%的概率出现第二条。问完美匹配(所有点都有匹配)方案数的期望*2^n。n<=15。
标程:
1 #include<cstdio> 2 #include<map> 3 using namespace std; 4 typedef long long ll; 5 const int mod=1e9+7; 6 const int inv2=5e8+4; 7 const int inv4=25e7+2; 8 const int inv_4=75e7+5; 9 const int N=1000; 10 int read() 11 { 12 int x=0;char ch=getchar(); 13 while (ch<'0'||ch>'9') ch=getchar(); 14 while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 15 return x; 16 } 17 struct node{int v,p;node(){}node(int A,int B){v=A;p=B;}}e[N]; 18 map<int,int> mp; 19 int n,m,op,x,y,cnt,id,id2; 20 int dp(int zt) 21 { 22 if (!zt) return 1; 23 map<int,int>::iterator t=mp.find(zt); 24 if (t!=mp.end()) return t->second; 25 int ans=0; 26 for (int i=1;i<=cnt;i++) 27 if ((e[i].v&zt)==e[i].v&&e[i].v*2>zt) 28 ans=((ll)ans+(ll)dp(zt^e[i].v)*e[i].p%mod)%mod; 29 return mp[zt]=ans; 30 } 31 int main() 32 { 33 n=read();m=read(); 34 for (int i=1;i<=m;i++) 35 { 36 op=read();x=read();y=read(); 37 id=(1<<x-1)|(1<<n+y-1); 38 e[++cnt]=node(id,inv2); 39 if (op) 40 { 41 x=read(),y=read(); 42 id2=(1<<x-1)|(1<<n+y-1); 43 e[++cnt]=node(id2,inv2); 44 if (!(id&id2))//有重合端点的话不用考虑 45 if (op==1) e[++cnt]=node(id|id2,inv4); 46 else e[++cnt]=node(id|id2,inv_4); 47 } 48 } 49 printf("%d ",((ll)dp((1<<(2*n))-1)<<n)%mod); 50 return 0; 51 }
易错点:1.注意2和3组的两条边如果有重复端点的话是不用考虑加组合边的,一定不会同时选这两条边。
题解:dp
如果都是1号组的点,也就是边没有依赖出现关系。那么直接dp。f[state]表示在state的匹配状态下完美匹配数的期望。
为了不算重,按照套路应该选取一个特殊点v,比如编号最大点、lowbit等。
f[state]=sigma(f[state^v^match_v]*p),p=50%,match_v表示与v相连的另一点。
状态数有sigma(C(n,i)^2)=sigma(C(n,i)*C(n,n-i))=范德蒙德卷积形式=C(2n,n)≈1.6*1e8,用map记录改成记忆化搜索就好了。
冷静分析第2组点和第3组点的特征:对于第2组点,如果两条边都不选或只选一条,概率则为0和50%和组1的情况一样;但当两条都选时,组1的概率为50%*50%=25%,而实际应该是50%,对策是再加一条包含这两条边的组合边,概率为25%,这样两种加起来就是50%了。
同理对于第3组点,如果都不选或只选一条,概率是等同于组1的选法。而都选的概率应该是0,于是加一条概率为-25%的组合边。
按照组1的情况转移即可。