Devu和鲜花 (容斥原理)

时间:2021-08-31 17:55:13

题目:

Devu有N个盒子,第i个盒子中有Ai枝花。

同一个盒子内的花颜色相同,不同盒子内的花颜色不同。

Devu要从这些盒子中选出M枝花组成一束,求共有多少种方案。

若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。

结果需对109+7取模之后方可输出。

输入格式

第一行包含两个整数N和M。

第二行包含N个空格隔开的整数,表示A1,A2,,AN

输出格式

输出一个整数,表示方案数量对109+7109+7取模后的结果。

数据范围

1N201≤N≤20,
0M10140≤M≤1014,
0Ai10120≤Ai≤1012

输入样例:

3 5
1 3 2

输出样例:

3

解题报告:
一开始没有研究出来怎么容斥处理,后来参考了一下大佬的博客,才理解了这道题目的真谛!

首先假设如果所有的fi都没有限制的情况。
根据无限多重集合的组合数那么答案即为C(s,n+s−1) 
我们需要从这个答案中减去不合法的情况。
不合法的情况即为xi>fixi>fi的情况。
设AiAi为xi>fixi>fi的方案。
|Ai|=C(s−(fi+1),n+s−1−(fi+1))
相当于我们预先放好这fi+1fi+1个元素。
同理|Ai⋂Aj|=C(s−(fi+1)−(fj+1),n+s−1−(fi+1)−(fj+1))
根据容斥原理答案即为
C(s,n+s−1)−∑1≤i≤n|Ai|+∑1≤i<j≤n|Ai⋂Aj|−...(省略)

我们可以通过状态压缩表示每个状态。
预处理每个状态需要预先放好几个元素。
最后一遍扫描来统计答案。
时间复杂度O(2nn)

 

ac代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 
 5 const int mod=1e9+7;
 6 int n,lim;
 7 ll s,ans;
 8 ll f[30],inv[30],sum[1100000];
 9 int num[1100000];
10 ll comb(ll n,ll m)
11 {//求解C(n,m)   
12     if(m<0)
13         return 0;
14     ll ret=1;
15     for(ll i=m+1;i<=n;i++)
16         ret=ret*(i%mod)%mod;//n!/m!
17     for(int i=1;i<=n-m;i++)
18         ret=ret*inv[i]%mod;//(n-m)!
19     return ret;
20 }
21 
22 int main()
23 {
24     cin>>n>>s;//n个盒子,挑选s朵花 
25     for(int i=0;i<n;i++)
26         cin>>f[i];//输入n个盒子中的花朵数目 
27     inv[1]=1;
28     for(int i=2;i<=n+1;i++)
29         inv[i]=(inv[mod%i]*(mod-mod/i))%mod;//神奇的求解逆元打表 
30     lim=(1<<n)-1;//二进制枚举 ,便于容斥 
31     for(int i=0;i<=lim;i++)
32     {
33         for(int j=0;j<n;j++)
34         {
35             if(i&(1<<j))
36             {
37                 sum[i]+=f[j]+1;// 预先放好fi+1个元素 
38                 num[i]++;//标记一下出现的次数 
39             }
40         }
41     }//已经处理好了所有的状态,从1,2,--> n 
42     for(int i=0;i<=lim;i++)
43     {
44         if(num[i]%2)
45             ans=(ans-comb(s+n-1-sum[i],s-sum[i])+mod)%mod;
46         else
47             ans=(ans+comb(s+n-1-sum[i],s-sum[i]))%mod;
48     }
49     cout<<ans<<endl;
50 }