题目:
Devu有N个盒子,第i个盒子中有Ai枝花。
同一个盒子内的花颜色相同,不同盒子内的花颜色不同。
Devu要从这些盒子中选出M枝花组成一束,求共有多少种方案。
若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。
结果需对109+7取模之后方可输出。
输入格式
第一行包含两个整数N和M。
第二行包含N个空格隔开的整数,表示A1,A2,…,AN。
输出格式
输出一个整数,表示方案数量对109+7109+7取模后的结果。
数据范围
1≤N≤201≤N≤20,
0≤M≤10140≤M≤1014,
0≤Ai≤10120≤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 }