【BZOJ4665】小w的喜糖
Description
废话不多说,反正小w要发喜糖啦!!
小w一共买了n块喜糖,发给了n个人,每个喜糖有一个种类。这时,小w突发奇想,如果这n个人相互交换手中的糖,那会有多少种方案使得每个人手中的糖的种类都与原来不同。
两个方案不同当且仅当,存在一个人,他手中的糖的种类在两个方案中不一样。
Input
第一行,一个整数n
接下来n行,每行一个整数,第i个整数Ai表示开始时第i个人手中的糖的种类
对于所有数据,1≤Ai≤k,k<=N,N<=2000
Output
一行,一个整数Ans,表示方案数模1000000009
Sample Input
6
1
1
2
2
3
3
1
1
2
2
3
3
Sample Output
10
题解:显然我们应该将每种糖果放在一起处理,用v[i]表示有多少人有第i种糖果。然后考虑容斥,用f[i][j]表示前i种糖果,至多j个人的糖果与原来不同的方案数,然后很容易DP求出f数组。
$f[i][j]=sumlimits_{k=0}^{v[i]}f[i-1][j-k]*C_{v[i]}^{k}*(v[i])*(v[i]-1)*...*(v[i]-k+1)$
发现我们在DP过程中并没有考虑我们选出来那j个人的顺序,所以最后f[i][j]乘上j!即可。最后因为每个糖果是相同的,所以方案数要除以v[i]!。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll P=1000000009; int n,m; ll ans; int col[2010],s[2010],v[2010]; ll c[2010][2010],f[2010][2010],jc[2010],ine[2010],jcc[2010]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(); int i,j,k; for(i=0;i<=n;i++) { c[i][0]=1; for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%P; } jc[0]=ine[0]=jcc[0]=jc[1]=ine[1]=jcc[1]=1; for(i=2;i<=n;i++) { jc[i]=jc[i-1]*i%P,ine[i]=P-(P/i)*ine[P%i]%P,jcc[i]=jcc[i-1]*ine[i]%P; } for(i=1;i<=n;i++) col[i]=rd(); sort(col+1,col+n+1); for(i=1;i<=n;i++) { if(col[i]>col[i-1]) m++; v[m]++; } for(i=1;i<=m;i++) s[i]=s[i-1]+v[i]; f[0][0]=1; for(i=1;i<=m;i++) for(j=0;j<=s[i-1];j++) for(k=0;k<=v[i];k++) f[i][j+k]=(f[i][j+k]+f[i-1][j]*c[v[i]][k]%P*jc[v[i]]%P*jcc[v[i]-k]%P)%P; for(i=0;i<=n;i++) { ans=(ans+((i&1)?-1:1)*f[m][i]*jc[n-i]+P)%P; } for(i=1;i<=m;i++) ans=ans*jcc[v[i]]%P; printf("%lld",ans); return 0; }