4665: 小w的喜糖
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 94 Solved: 53Description
废话不多说,反正小w要发喜糖啦!!小w一共买了n块喜糖,发给了n个人,每个喜糖有一个种类。这时,小w突发奇想,如果这n个人相互交换手中的糖,那会有多少种方案使得每个人手中的糖的种类都与原来不同。两个方案不同当且仅当,存在一个人,他手中的糖的种类在两个方案中不一样。Input
第一行,一个整数n接下来n行,每行一个整数,第i个整数Ai表示开始时第i个人手中的糖的种类对于所有数据,1≤Ai≤k,k<=N,N<=2000Output
一行,一个整数Ans,表示方案数模1000000009Sample Input
6
1
1
2
2
3
3Sample Output
10HINT
Source
【分析】
DP+容斥类的题目都不是很会做啊QWQ。
人不分顺序,同一种糖果要一起做。
f[i][j]表示前i种糖果,至少有j个人的是不合法的方案数。
f[i][j]=f[i-1][j-k]*c[a[i]][k]*(a[i]*(a[i]-1)*(a[i]-2)**[乘k次])
最后把其他的全排列f[n][i]=f[n][i]*(n-i)!
然后容斥,if(i&1) ans-=f[n][i] else ans+=f[n][i];
然后ans/(a[i]!)
先把所有糖果都看成不一样的,最后除以每种糖果的数量的阶乘,就能保证本质不同了。
ORZ Claris。。
学会了不用全部开LL的方法了,在前面加一个1LL* 然后每次乘完就Mod
中间用到了线性求逆元,复习一下:ny[i]=(Mod-Mod/i)*ny[Mod%i]%Mod
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define Mod 1000000009
#define Maxn 2010
#define LL long long int a[Maxn],c[Maxn][Maxn],pw[Maxn],ny[Maxn];
int f[Maxn][Maxn]; int main()
{
int n;
scanf("%d",&n);
memset(a,,sizeof(a));
for(int i=;i<=n;i++)
{
int x;
scanf("%d",&x);
a[x]++;
}
for(int i=;i<=n;i++) c[i][]=;
for(int i=;i<=n;i++)
for(int j=;j<=i;j++)
{
c[i][j]=(c[i-][j-]+c[i-][j])%Mod;
}
int ans=;
pw[]=;
for(int i=;i<=n;i++) pw[i]=1LL*pw[i-]*i%Mod;
ny[]=ny[]=;
for(int i=;i<=n;i++) ny[i]=1LL*(Mod-Mod/i)*ny[Mod%i]%Mod;
for(int i=;i<=n;i++) ny[i]=1LL*ny[i]*ny[i-]%Mod;
memset(f,,sizeof(f));
f[][]=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
for(int k=;k<=a[i];k++)
{
if(k>j) break;
f[i][j]=(f[i][j]+1LL*f[i-][j-k]*c[a[i]][k]%Mod*pw[a[i]]%Mod*ny[a[i]-k]%Mod)%Mod;
}
for(int i=;i<=n;i++)
{
if(i&) ans-=1LL*f[n][i]*pw[n-i]%Mod;
else ans+=1LL*f[n][i]*pw[n-i]%Mod;
ans=(ans%Mod+Mod)%Mod;
}
for(int i=;i<=n;i++) if(a[i]) ans=(1LL*ans*ny[a[i]])%Mod;
printf("%d\n",ans);
return ;
}
2017-04-11 14:25:31