版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83613231
1题目大意:给出n个盒子,每个盒子里有val[i]个球,想要拿出s个球,从每个盒子里拿出相同的球算同种方案,求方案数。
首先对于式子的非负整数解个数为,考虑隔板法,将s个东西分成n份,允许有空
相当于在s-1个空中放n-1个隔板,有空的盒子可以相当于先加上盒子个数个小球,真实计算是再减去,所以就是n+s个东西
其中n+s-1个空选择n-1个放。但是这道题有个数限制,所以不合法的情况就是其中的盒子放多了,因为放多了1个就不合法
所以设不合法的情况为val[i]+1(PS:之后再放自己里都不合法,所以不必计算,也不少不重复)。那么减去所有1个盒子放
多的情况时,多减去了2个放多的情况。所以要用容斥原理,减去奇数个放多的情况,对于组合数可以枚举,因为n≤20。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define mode 1000000007
using namespace std;
typedef long long ll;
int n;
ll s,val[25],ans,now,gs,niv[25];
ll read()
{
ll x=0,f=1;char ch;
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
ll C(ll x,ll y)
{
if(x<y)return 0;
ll as=1;
for(ll i=x-y+1;i<=x;i++)
{
as=(as*(i%mode))%mode;
}
return as*niv[y]%mode;
}
int main()
{
scanf("%d%lld",&n,&s);
for(int i=1;i<=n;i++)
{
val[i]=read();
}
niv[1]=1;
for(int i=2;i<=n;i++)niv[i]=(mode-mode/i)*niv[mode%i]%mode;
niv[0]=1;
for(int i=1;i<=n;i++)niv[i]=niv[i-1]*niv[i]%mode;
for(int i=0;i<=(1<<n)-1;i++)
{
gs=0,now=0;
for(int j=1;j<=n;j++)
{
if(1<<(j-1)&i)
{
gs++;now+=val[j]+1;
}
}
if(gs%2==0)ans+=C(s+n-now-1,n-1),ans%=mode;
else ans-=C(s+n-now-1,n-1),ans%=mode;
}
printf("%lld",(ans%mode+mode)%mode);
return 0;
}