【BZOJ4665】小w的喜糖 容斥+組合數
阿新 • • 發佈:2017-10-15
原來 ans lin log style pre 交換 amp rip
6
1
1
2
2
3
3
【BZOJ4665】小w的喜糖
Description
廢話不多說,反正小w要發喜糖啦!! 小w一共買了n塊喜糖,發給了n個人,每個喜糖有一個種類。這時,小w突發奇想,如果這n個人相互交換手中的糖,那會有多少種方案使得每個人手中的糖的種類都與原來不同。 兩個方案不同當且僅當,存在一個人,他手中的糖的種類在兩個方案中不一樣。Input
第一行,一個整數n 接下來n行,每行一個整數,第i個整數Ai表示開始時第i個人手中的糖的種類 對於所有數據,1≤Ai≤k,k<=N,N<=2000Output
一行,一個整數Ans,表示方案數模1000000009Sample Input
1
1
2
2
3
3
Sample Output
10題解:顯然我們應該將每種糖果放在一起處理,用v[i]表示有多少人有第i種糖果。然後考慮容斥,用f[i][j]表示前i種糖果,至多j個人的糖果與原來不同的方案數,然後很容易DP求出f數組。
$f[i][j]=\sum\limits_{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; }
【BZOJ4665】小w的喜糖 容斥+組合數