@xiaoziyao
2020-08-02T16:42:12.000000Z
字数 1673
阅读 1172
解题报告
题意:种颜色,每种颜色朵花,需要选择朵花,求有多少种选择方案(两个方案不同当且仅当存在某一种颜色的花数量不同),对取模。
数据范围:。
这是一道求多重集合组合数的题目,比较好地考察了容斥原理和组合数计算。
可以抽象出模型:求在一个存在个元素,每个元素有个的可重集取出个元素的方案数。
我们令为满足第个元素选择的数量在以内,且取出了个元素的方案形成的集合,那么我们的答案便是。
因为是求至多选多少个,所以不是很好求,那么先容斥一波,
全集就是种元素取出个元素的方案数(某个元素可以选出个),直接套公式:(具体可以考虑一种特殊的隔板法,加入个球,选择个球间隔出个区间,这些区间可以为)
考虑如何求并集,还是不好求,所以继续容斥:
因为很小,所以可以考虑二进制枚举每一种情况,然后问题转移成怎么求某一种元素选的次数要超过并取出个元素的方案数。
因为起码要选个这种元素,所以可以把选出的减去,这样就转化为全集那里讲的公式了。
然后直接套公式,套在容斥原理里面,就ok了。
最后,因为很大,所以可以吧组合数拆开,变为,然后预处理一下就可以了。
#include<stdio.h>
const long long maxn=25,mod=1000000007;
long long i,j,k,m,n,mul=1,ans;
long long finv[maxn],a[maxn];
long long ksm(long long a,long long b){
long long res=1;
while(b){
if(b&1)
res=res*a%mod;
a=a*a%mod,b>>=1;
}
return res;
}
long long c(long long n,long long m){
if(n<0||m<0||m>n)
return 0;
n%=mod,m%=mod;
long long res=1;
for(long long i=n-m+1;i<=n;i++)
res=res*i%mod;
res=res*finv[m]%mod;
return res;
}
int main(){
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(i=1;i<=n;i++)
mul=mul*i%mod;
finv[n]=ksm(mul,mod-2);
for(i=n;i>=1;i--)
finv[i-1]=finv[i]*i%mod;
ans=c(n+m-1,n-1);
for(k=1;k<(1<<n);k++){
long long cnt=0,res=n+m-1;
for(i=1;i<=n;i++)
if((k>>(i-1))&1)
cnt++,res-=a[i]+1;
ans=(ans-(cnt%2==0? -1:1)*c(res,n-1))%mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}