bzoj4665:小w的喜糖

传送门

计数( m dp),容斥

首先,我们可以将每个数都看成本质不同的

然后我们设(f(i,j))表示当前考虑了前(i)种颜色,有(j)个位置保留的是原来的数,设( m cnt[i])为第( m i)种颜色的数量

那么转移显然就是

[f(i,j)=sum_{k=0}^{min(cnt[i],j)}f(i-1,j-k)*inom{cnt[i]}{k}*A^{k}_{cnt[i]} ]

由于现在所有数本质不同,所以我们可以容斥了,设( m m)为颜色总数

[ans=sum_{i=0}^{n}(-1)^if(m,i)*(n-i)! ]

由于我们一开始将所有颜色看成是本质不同的了,所以我们还需要将( m ans)除以所有的( m cnt[i])

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
	char ch; bool ok;
	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=2010,mod=1e9+9;
int n,a[maxn],tot,ans,t[maxn],fac[maxn],inv[maxn],m,f[maxn][maxn];
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
bool cmp(int x,int y){return x>y;}
int mi(int a,int b){int ans=1;while(b){if(b&1)ans=mul(ans,a);b>>=1,a=mul(a,a);}return ans;}
int C(int n,int m){return mul(fac[n],mul(inv[n-m],inv[m]));}
int A(int n,int m){return mul(fac[n],inv[n-m]);}
int main(){
	read(n),inv[0]=fac[0]=f[0][0]=1;
	for(rg int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
	inv[n]=mi(fac[n],mod-2);
	for(rg int i=n-1;i;i--)inv[i]=mul(inv[i+1],i+1);
	for(rg int i=1;i<=n;i++)read(a[i]),t[a[i]]++;
	sort(t+1,t+n+1,cmp);for(rg int i=1;i<=n+1;i++)if(!t[i]){m=i-1;break;}
	for(rg int i=1,tot=t[1];i<=m;i++,tot+=t[i])
		for(rg int j=0;j<=tot;j++)
			for(rg int k=0;k<=j&&k<=t[i];k++)
				f[i][j]=add(f[i][j],mul(f[i-1][j-k],mul(C(t[i],k),A(t[i],k))));
	for(rg int i=0;i<=n;i++)if(i&1)ans=del(ans,mul(f[m][i],fac[n-i]));else ans=add(ans,mul(f[m][i],fac[n-i]));
	for(rg int i=1;i<=m;i++)ans=mul(ans,inv[t[i]]);
	printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/lcxer/p/11059315.html