bzoj4665:小w的喜糖

传送门

计数\(\rm dp\),容斥

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

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

那么转移显然就是
\[ f(i,j)=\sum_{k=0}^{min(cnt[i],j)}f(i-1,j-k)*\binom{cnt[i]}{k}*A^{k}_{cnt[i]} \]
由于现在所有数本质不同,所以我们可以容斥了,设\(\rm m\)为颜色总数
\[ ans=\sum_{i=0}^{n}(-1)^if(m,i)*(n-i)! \]
由于我们一开始将所有颜色看成是本质不同的了,所以我们还需要将\(\rm ans\)除以所有的\(\rm 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\n",ans);
}
posted @ 2019-06-20 16:03 蒟蒻--lichenxi 阅读(...) 评论(...) 编辑 收藏