N个盒子,每个盒子里面有一种颜色的花,数量为si,N个盒子里花的颜色各不相同,但同一个盒子里面的花认为是相同的,现在需要选出K朵花,有多少种不同的方案?由于结果很大,输出Mod 1000000007的结果即可。
例如:3个盒子,花的数量分别为1 3 2,需要选出5朵花,那么可以有如下3种方案:{1, 2, 2}, {0, 3, 2}, {1, 3, 1}。
一开就是组合数的题目,但是要怎么做呢。
如果没有个数限制的话直接用隔板法 Cn−1n+m−1 ,相当于把m种颜色填入n个盒子里。
但是有一些盒子可能会爆满,这个时候这个盒子就要强制让它满(设除了这些盒子的总空间为sum),那么就是 Cn−1n+sum−1 ,让这些盒子强制性满,然后其他随便填,那么它的子集肯定包含这些盒子全满的情况。
但是这样要每个状态都涉及到的话,那么就要用容斥来做了:强制0个(+),强制1个(-),强制2个(+)……
组合数中的东西很大,要用lucas定理,但是因为模数很大,还是很麻烦,但是一般都能表现良好。
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int mo=1e9+7;
typedef long long ll;
ll i,j,k,l,t,n,m,ans;
ll a[27],er[27],bz,sum;
ll qsm(ll x,ll y){
ll z=1;
for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;
return z;
}
ll suan(ll x,ll y){
if(xreturn 0;
ll a=1,b=1,i;
if(y1,x)a=a*i%mo;
fo(i,2,x-y)b=b*i%mo;
return a*qsm(b,mo-2)%mo;
}
ll c(ll x,ll y){
if(!y)return 1;
else return c(x/mo,y/mo)*suan(x%mo,y%mo)%mo;
}
int main(){
// freopen("fan.in","r",stdin);
er[0]=1;fo(i,1,20)er[i]=er[i-1]*2;
scanf("%lld%lld",&n,&m);
fo(i,1,n)scanf("%lld",&a[i]);
fo(i,0,er[n]-1){
bz=1;sum=m;
fo(j,1,n){
if(i&er[j-1]){
bz*=-1;
sum-=a[j]+1;
}
}
if(sum<0)continue;
(ans+=bz*c(n+sum-1,n-1))%=mo;
}
ans=(ans+mo)%mo;
printf("%lld\n",ans);
}