CCA的小球【容斥定理】

题意

给定 \(n\) 个小球,每个小球有颜色,要将它们摆成一行 。两个方案不同,当且仅当存在某个位置,两种方案摆在这个位置的小球颜色不同。一个方案合法,当且仅当不存在任意两个位置相邻的小球颜色相同,求合法方案数对 \(10^9+7\) 取模后的值。\((n\leq 10^6)\),每种颜色出现的次数 \(\leq 2\)
链接:https://ac.nowcoder.com/acm/contest/11168/D

分析

发现合法方案不好求解。因此,可以求出不合法的方案的数量,从总方案中减去即为合法方案数。在计算不合法方案数时,对相邻同色球的对数进行讨论。会发现出现重复,采用容斥定理来去重。计算是注意可重集合的排列数计算。代码实现时,一开始使用离散化的方式,但一直错。换成直接排序判断相邻是否相同就过了。

代码

#include <bits/stdc++.h>

using namespace std;
const int mod=1e9+7;
const int N=1e6+6;
int a[N],fac[N],inv[N],num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    fac[0]=1,inv[1]=1;
    for(int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%mod;
    for(int i=2;i<=n;i++) inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
    sort(a+1,a+1+n);
    int ans=0,m=0,pw=1,res=1;
    for(int i=2;i<=n;i++)
        if(a[i]==a[i-1]) m++;
    for(int i=1;i<=m;i++) pw=1LL*pw*inv[2]%mod;
    ans=1LL*fac[n]*pw%mod;
    for(int i=1;i<=m;i++)
    {
        res=1LL*res*(m-i+1)%mod;
        res=1LL*res*inv[i]%mod;
        pw=2LL*pw%mod;
        if(i&1) ans=(ans-1LL*res*fac[n-i]%mod*pw%mod)%mod;
        else ans=(ans+1LL*res*fac[n-i]%mod*pw%mod)%mod;
        ans=(ans+mod)%mod;
    }
    printf("%d\n",(ans+mod)%mod);
    return 0;
}

posted @ 2021-03-15 10:24  xzx9  阅读(85)  评论(0编辑  收藏  举报