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
",(ans+mod)%mod);
        
    
    return 0;
}
原文地址:https://www.cnblogs.com/AKCqhzdy/p/10442237.html