[前缀和][容斥原理] Jzoj P4196 二分图计数

Description
 
Input
Output
 
Sample Input
样例1
1 2
0

样例2
3 3
0
1
2
Sample Output
样例1
1

样例2
70
 
Data Constraint

 

 

题解

  • 求二分图完备匹配的个数,左边每个点只和右边一个点不相连
  • 可以看成先求出全匹配的方案数减去不连点的边对答案的贡献
  • 就是容斥原理
  • 先枚举集合,然后枚举其子集,表示将违反的限制条件的集合
  • 最后算出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 }

 

转载于:https://www.cnblogs.com/Comfortable/p/9275524.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值