[前缀和][容斥原理] Jzoj P4196 二分图计数
题解
- 求二分图完备匹配的个数,左边每个点只和右边一个点不相连
- 可以看成先求出全匹配的方案数减去不连点的边对答案的贡献
- 就是容斥原理
- 先枚举集合,然后枚举其子集,表示将违反的限制条件的集合
- 最后算出ans
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 using namespace std; 7 const long long mo=1e9+7; 8 struct edge{ long long v,d; }a[18]; 9 bool bz[70000],vis[18]; 10 long long n,m,tot,k[18],ans1,f[18][18],i,j,mi[18],s1,s2,sum[70000],s,ans[70000]; 11 bool cmp(edge x,edge y) { return x.v<y.v; } 12 void dfs(long long t,long long x) 13 { 14 if (t>n) 15 { 16 bz[x]=true; 17 return; 18 } 19 if (vis[k[t]]) 20 { 21 vis[k[t]]=false; 22 dfs(t+1,x+mi[t-1]); 23 vis[k[t]]=true; 24 } 25 dfs(t+1,x); 26 } 27 int main() 28 { 29 freopen("bipartite.in","r",stdin); 30 freopen("bipartite.out","w",stdout); 31 scanf("%lld%lld",&n,&m); 32 for (int i=1;i<=n;i++) 33 { 34 scanf("%lld",&a[i].v); 35 a[i].d=i; 36 } 37 sort(a+1,a+n+1,cmp); 38 k[a[1].d]=tot=1; 39 for (int i=2;i<=n;i++) 40 { 41 if (a[i].v>a[i-1].v) tot++; 42 k[a[i].d]=tot; 43 } 44 for (int i=1;i<=17;i++) 45 { 46 f[i][i-1]=1; 47 for (int j=i;j<=17;j++) f[i][j]=(f[i][j-1]*(m-j+1))%mo; 48 } 49 mi[0]=1; 50 for (int i=1;i<=n;i++) mi[i]=mi[i-1]*2; 51 memset(vis,1,sizeof(vis)); 52 dfs(1,0); 53 for (int i=1;i<=mi[n]-1;i++) 54 for (int j=1;j<=n;j++) 55 if (mi[j-1]&i) sum[i]++; 56 for (int i=1;i<=mi[n]-1;i++) 57 { 58 j=i; 59 while (j>-1) 60 { 61 j&=i; 62 if (bz[j]) 63 { 64 s=f[sum[j]+1][sum[i]]; 65 if (sum[j]%2==1) ans[i]=(ans[i]-s+mo)%mo; else ans[i]=(ans[i]+s)%mo; 66 } 67 j--; 68 } 69 } 70 for (int i=1;i<=mi[n]-1;i++) ans1=(ans1+ans[i]*i)%mo; 71 printf("%lld",ans1); 72 return 0; 73 }