codeforces 451E Devu and Flowers
阿新 • • 發佈:2018-11-06
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; }