1. 程式人生 > >bzoj4710 [Jsoi2011]分特產 容斥+組合數

bzoj4710 [Jsoi2011]分特產 容斥+組合數

Description

JYY 帶隊參加了若干場ACM/ICPC 比賽,帶回了許多土特產,要分給實驗室的同學們。
JYY 想知道,把這些特產分給N 個同學,一共有多少種不同的分法?當然,JYY 不希望任
何一個同學因為沒有拿到特產而感到失落,所以每個同學都必須至少分得一個特產。
例如,JYY 帶來了2 袋麻花和1 袋包子,分給A 和B 兩位同學,那麼共有4 種不同的
分配方法:
A:麻花,B:麻花、包子
A:麻花、麻花,B:包子
A:包子,B:麻花、麻花
A:麻花、包子,B:麻花

N, M 不超過1000,每一種特產的數量不超過1000

Output
輸出一行,不同分配方案的總數。由於輸出結果可能非常巨大,你只需要輸出最終結果
MOD 1,000,000,007 的數值就可以了。

Solution

每種至少有一個,那麼答案就是至少0個沒有的-至少1個沒有的+至少2個沒有的。。。
至少i個沒有的就是j=1m(a[j]+i1i1)\prod_{j=1}^m {\binom{a[j]+i-1}{i-1}},等價於把a[j]個相同的球放進n-i個不同的盒子裡

Code

#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int MOD=1000000007;
const int N=20005
; int ny[N],fac[N]; int a[N]; int C(int n,int m) { return 1LL*fac[n]*ny[m]%MOD*ny[n-m]%MOD; } int ksm(int x,int dep) { int res=1; for (;dep;dep>>=1) { (dep&1)?(res=1LL*res*x%MOD):0; x=1LL*x*x%MOD; } return res; } int main(void) { fac[0]=1; rep(i,1,N-1) fac[i]=1LL*fac[i-1]*i%MOD;
rep(i,0,N-1) ny[i]=ksm(fac[i],MOD-2); int n,m; scanf("%d%d",&n,&m); rep(i,1,m) scanf("%d",&a[i]); int ans=0; rep(i,0,n-1) { int tot=C(n,i); rep(j,1,m) { tot=1LL*tot*C(a[j]+n-i-1,n-i-1)%MOD; } if (i&1) ans=(ans-tot+MOD)%MOD; else ans=(ans+tot)%MOD; } printf("%d\n", ans); return 0; }