bzoj4710: [Jsoi2011]分特产(容斥原理)

传送门
题意简述:有 n n n个人, m m m种物品,给出每种物品的数量 a i a_i ai,问每个人至少分得一个物品的方案数( n , m , 每 种 物 品 数 ≤ 1000 n,m,每种物品数\le1000 n,m,1000)。


思路:
我们算出 f i f_i fi表示至少 i i i个人没有分到物品的方案数容斥一下即可。
于是 f i = C n i ∏ j = 1 m C n − i − 1 + a j n − i − 1 f_i=C_n^i\prod_{j=1}^mC_{n-i-1+a_j}^{n-i-1} fi=Cnij=1mCni1+ajni1
前面的组合数指的是选出没有分到物品的 i i i个人,后面的指对于每一种物品分给剩下的 n − i n-i ni个人的方案数。
代码:

#include
#define ri register int
using namespace std;
typedef long long ll;
const int N=2005,mod=1e9+7,mod1=1e9+6;
int ans=0,fac[N],ifac[N],n,m,a[N];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int C(int n,int m){return mul(mul(fac[n],ifac[m]),ifac[n-m]);}
inline void init(){
	fac[0]=ifac[0]=fac[1]=ifac[1]=1;
	for(ri i=2;i<=2000;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
	for(ri i=2;i<=2000;++i)ifac[i]=mul(ifac[i],ifac[i-1]);
}
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	n=read(),m=read(),init();
	for(ri i=1;i<=m;++i)a[i]=read();
	for(ri tmp,i=0;i<=n;++i){
		tmp=C(n,i);
		for(ri j=1;j<=m;++j)tmp=mul(tmp,C(n-i-1+a[j],n-i-1));
		ans=i&1?dec(ans,tmp):add(ans,tmp);
	}
	cout<<ans;
	return 0;
}

你可能感兴趣的:(#,容斥原理,#,数学)