bzoj4665: 小w的喜糖
我有点懵逼瞎容斥就过了
首先看了下路牌就是容斥
明显位置没有用,排一波序把拿相同颜色的球的人放一起
设f[i][j]表示枚举到第i个颜色,至少j个人拿了和原来一样颜色的球
那么f[i+1][j+k]=(f[i+1][j+k]+f[i][j]*C[p[i+1]][k]%mod*C[(ls+p[i+1])-(j+k)][p[i+1]-k])%mod;
p是当前球数,ls是前面球数的和,加起来相当于当前总球数
C[p[i+1]][k]是选k个人拿和原来一样颜色的球,C[(ls+p[i+1])-(j+k)][p[i+1]-k]是在剩下的位置中把没有拿的球找个位置放好
考虑这里的容斥系数,也就是对于重复人数为d的一个方案,对f[n][k]的贡献次数
开始我想成了每个不同颜色的球的组合数加起来刚好等于k,实际上可以合并起来看,在d个球中选出k个已确定,就是C[d][k]
那么上二项式反演即可
由于C[i][0]=1,网上很多人都说系数就是1,直接+1-1...................
容斥的时候输出答案记得要(ans+mod)%mod啊,系数有-1的啊!!!!!!!!!!!!!!!!
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int _=1e2; const int maxn=2*1e3+_; const int maxm=2*1e3+_; const LL mod=1e9+9; LL C[maxn][maxn]; void yu() { C[0][0]=1; for(int i=1;i<maxn;i++) { C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } } int a[maxn],m,p[maxm]; LL f[maxm][maxn];//前i个数中,相同个数至少为j的个数 int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); yu(); int n; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+n+1); m=1,p[1]=1; for(int i=2;i<=n;i++) { if(a[i]!=a[i-1])m++; p[m]++; } int ls=0; f[0][0]=1; for(int i=0;i<m;i++) { for(int j=0;j<=ls;j++) if(f[i][j]) { for(int k=0;k<=p[i+1];k++) f[i+1][j+k]=(f[i+1][j+k]+f[i][j]*C[p[i+1]][k]%mod*C[(ls+p[i+1])-(j+k)][p[i+1]-k])%mod; } ls+=p[i+1]; } LL ans=0;int u=1; for(int i=0;i<=n;i++) { ans=(ans+u*C[i][0]*f[m][i])%mod; u=-u; } printf("%lld\n",(ans+mod)%mod); return 0; }
pain and happy in the cruel world.