BZOJ4665 : 小w的喜糖
考虑枚举哪些人一定不合法,那么方案数可以通过简单的排列组合算出。
于是设$f[i][j]$表示前$i$种糖果,一共有$j$个人一定不合法的方案数,但是这样并不能保证其他人一定合法,所以需要进行容斥。
最后将答案除以每种糖果数量的阶乘,即可保证本质不同。
时间复杂度$O(n^2)$。
#include<cstdio> const int N=2005,P=1000000009; int n,i,j,k,x,a[N],C[N][N],fac[N],inv[N],f[N][N],tmp,ans; int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&x),a[x]++; for(C[0][0]=i=1;i<=n;i++)for(C[i][0]=j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%P; for(fac[0]=fac[1]=inv[0]=inv[1]=1,i=2;i<=n;i++){ fac[i]=1LL*fac[i-1]*i%P; inv[i]=1LL*(P-inv[P%i])*(P/i)%P; } for(i=2;i<=n;i++)inv[i]=1LL*inv[i]*inv[i-1]%P; for(f[0][0]=i=1;i<=n;i++)for(j=0;j<=n;j++)for(k=0;k<=a[i]&&k<=j;k++) f[i][j]=(1LL*f[i-1][j-k]*C[a[i]][k]%P*fac[a[i]]%P*inv[a[i]-k]+f[i][j])%P; for(i=0;i<=n;i++){ tmp=1LL*f[n][i]*fac[n-i]%P; if(i&1)ans=(ans-tmp+P)%P;else ans=(ans+tmp)%P; } for(i=1;i<=n;i++)ans=1LL*ans*inv[a[i]]%P; return printf("%d",ans),0; }