BZOJ 4665: 小w的喜糖
Sol
DP+容斥.
这就是一个错排的扩展...可是想到容斥却仅限于种数的容斥,如果种数在一定范围内我就会做了QAQ.
但是容斥的是一定在原来位置的个数.
发现他与原来的位置无关,可以先把每个同种的糖看成本质不同的来DP.
\(f[i][j]\) 表示前 \(i\) 种糖,一定有 \(j\) 个不合法的方案数.
然后在第 \(i\) 种内枚举有几种不合法的状态.这样就可以从前 \(i-1\) 种转移了.
然后随便搞搞组合数,还有线性计算逆元,上篇随笔里提到了.
Code
/************************************************************** Problem: 4665 User: BeiYu Language: C++ Result: Accepted Time:2548 ms Memory:32720 kb ****************************************************************/ #include<cstdio> #include<iostream> using namespace std; const int N = 2005; const int Mo = 1000000009; typedef long long LL; int n,m;LL ans; int a[N],fac[N],inv[N],C[N][N]; int f[N][N]; inline int in(int x=0,char ch=getchar()){ while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; } int main(){ // freopen("in.in","r",stdin); // freopen("out.out","w",stdout); n=in(),fac[0]=fac[1]=1,C[0][0]=1,f[0][0]=1; inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=(LL)(Mo-Mo/i)*inv[Mo%i]%Mo,fac[i]=(LL)fac[i-1]*i%Mo; // for(int i=1;i<=n;i++) cout<<inv[i]<<" ";cout<<endl; for(int i=2;i<=n;i++) inv[i]=(LL)inv[i-1]*inv[i]%Mo; for(int i=1;i<=n;i++) for(int j=0;j<=i;j++) if(!j) C[i][j]=1;else C[i][j]=(LL)(C[i-1][j-1]+C[i-1][j])%Mo; for(int i=1;i<=n;i++) a[in()]++; for(int i=1;i<=n;i++) if(a[i]) a[++m]=a[i]; for(int i=1;i<=m;i++) for(int j=0;j<=n;j++) for(int k=0;k<=a[i]&&k<=j;k++) f[i][j]=(LL)(f[i][j]+(LL)f[i-1][j-k]*C[a[i]][k]%Mo*fac[a[i]]%Mo*inv[a[i]-k])%Mo; for(int i=0;i<=n;i++) if(i&1) ans=(ans-(LL)f[m][i]*fac[n-i]%Mo+Mo)%Mo;else ans=(ans+(LL)f[m][i]*fac[n-i]%Mo)%Mo; for(int i=1;i<=m;i++) ans=ans*inv[a[i]]%Mo; cout<<ans<<endl; // for(int i=1;i<=n;i++) for(int j=0;j<=n;j++) printf("%d%c",f[i][j]," \n"[j==n]); return 0; }