bzoj 4710 : [Jsoi2011]分特产
好久没做组合的题竟然懵逼了好长时间,去吃了顿饭就突然会做了。。。
如果没有每个人至少一个的限制的话那么答案显然是∏(c(n-1,a[i]+n-1)),相当于把每一种物品排成一排然后每排放(n-1)个隔板,第i个隔板和第i+1个隔板之间的物品就是第i个人的物品,显然每种方案对应着一种实际方案(会组合的人就当我是在废话吧)。
那么如果加上限制呢,第一反应显然是容斥,上一段算出的答案可能有一个人没有,那就减去n-1个人的所有合法方案(没有空的人)*n(枚举谁没有),如果有两个人没有那就减去n-2个人的合法方案*c(n,2),这东西显然可以递推,那就做完了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 #define N 2005 7 using namespace std; 8 int read() 9 { 10 int p=0;char c=getchar(); 11 while(c<'0'||c>'9')c=getchar(); 12 while(c>='0'&&c<='9')p=p*10+c-'0',c=getchar(); 13 return p; 14 } 15 const int p = 1000000007; 16 int c[N][N]; 17 void yu() 18 { 19 for(int i=0;i<=2000;i++) 20 { 21 c[i][0]=1; 22 for(int j=1;j<=i;j++) 23 { 24 c[i][j]=(c[i-1][j-1]+c[i-1][j])%p; 25 } 26 } 27 return ; 28 } 29 int n,m; 30 int a[N]; 31 int f[N]; 32 int main() 33 { 34 scanf("%d%d",&n,&m); 35 yu(); 36 for(int i=1;i<=m;i++)scanf("%d",&a[i]); 37 for(int i=1;i<=n;i++) 38 { 39 f[i]=1; 40 for(int j=1;j<=m;j++) 41 { 42 f[i]=((ll)f[i]*c[a[j]+i-1][i-1])%p;; 43 } 44 for(int j=1;j<i;j++) 45 { 46 f[i]=(f[i]-((ll)f[j]*c[i][i-j])%p+p)%p; 47 } 48 } 49 printf("%d\n",f[n]); 50 return 0; 51 }