Bzoj4710--Jsoi2011分特产
组合计数+容斥原理的题目
考虑对于每一类特产分给所有人有多少种方式,就是把每类特产分给所有人的方案数乘积
把x个物品分给n个人的方案数可以看成n-1个分隔符和x个物品的排列数,即为n-1+x个物品中选出x个物品的方案数,即C(x,x+n-1)
但是这样会把不合法的状态也算入答案,所以可以容斥一波
n个人中有a个人没分到东西的方案数即为把x个物品分给n-a个人的方案数,最后再乘上n个人中选a个人的方案数即可
代码:
#include<bits/stdc++.h> #define INF 1000000000 #define LNF 100000000000000ll #define eps 1e-9 #define LL long long #define MOD 1000000007 inline int _max(int a,int b) {return a>b?a:b;} inline double _fabs(double a) {return a>0?a:-a;} using namespace std; #define MAXN 1005 #define MAXM 30005 LL C[MAXN*2][MAXN*2],ans; int n,m,a[MAXN],mx; void Get_C() { int r=n+mx-1; for(int i=0;i<=r;i++) C[i][0]=1; for(int i=0;i<=r;i++) C[0][i]=1; for(int i=1;i<=r;i++) for(int j=1;j<=r;j++) C[i][j]=(C[i-1][j]+C[i][j-1])%MOD; } LL comp(int v) { LL ret=C[v][n-v];if(v&1) ret=-ret; for(int i=1;i<=m;i++) ret=ret*C[a[i]][n-1-v]%MOD; return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d",&a[i]),mx=_max(mx,a[i]); Get_C(); for(int i=0;i<=n;i++) { ans=(ans+comp(i))%MOD; if(ans<0) ans+=MOD*(-ans/MOD+1); } cout<<ans<<endl; return 0; }