BZOJ 4665: 小w的喜糖【dp,容斥

……QwQ做的第一发这种题

f[i][j]表示分配了前i种,至少有j个人不合法,然后容斥一下就好

最后统计的时候,剩下的n-j个人的分配方法是(n-j)!除以每种糖剩余数量的阶乘的积,这个积直接在dp的时候算好


#include
#define MOD 1000000009
#define MAXN 2005
using namespace std;	int n;
int x[MAXN];
int C[MAXN][MAXN];
int jc[MAXN],_jc[MAXN];

int f[MAXN][MAXN];

inline int POW(long long d,long long c){
	long long rtn=1;
	for(;c;c>>=1,d=d*d%MOD)
		if(c&1)	rtn=rtn*d%MOD;
	return (int)rtn;
}

inline void Plus(int &a,const int b){
	a+=b;
	if(a<0)	a+=MOD;
	if(a>=MOD)	a-=MOD;
}

inline void init(){
	C[0][0]=1;
	for(register int i=1,j;i<=n;++i)
		for(j=1,C[i][0]=1;j<=i;++j)
			C[i][j]=C[i-1][j-1],Plus(C[i][j],C[i-1][j]);
	jc[0]=1;
	for(register int i=1;i<=n;++i)	jc[i]=1ll*jc[i-1]*i%MOD;
	_jc[n]=POW(jc[n],MOD-2);
	for(register int i=n-1;~i;--i)	_jc[i]=1ll*_jc[i+1]*(i+1)%MOD;
}

int read_x;
int main(){
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	scanf("%d",&n);
	
	init();
	for(int i=1;i<=n;++i)	scanf("%d",&read_x),++x[read_x];
	int tot=0;
	f[0][0]=1;
	for(int i=1;i<=n;tot+=x[i++])
		for(int j=0;j<=tot;++j)
			for(int k=0;k<=x[i];++k)
				Plus(f[i][j+k],1ll*f[i-1][j]*C[x[i]][k]%MOD*_jc[x[i]-k]%MOD);//,printf("f[ %d ][ %d ] = %d\n",i,j+k,f[i][j+k]);
	int ans=0;
//	for(int i=0;i<=tot;++i)	printf("%d\n",f[n][i]);
	for(int i=0;i<=tot;++i)
		Plus(ans,(i&1?-1ll:1ll)*f[n][i]*jc[tot-i]%MOD);
	printf("%d",ans);
	return 0;
}




你可能感兴趣的:(OI,BZOJ,TEST,组合数学,容斥,计数dp)