牛客练习赛78 D CCA的小球

https://ac.nowcoder.com/acm/contest/11168/D

 

借助这个公式:

有重复集合的排列:

定理:设S是多重集合,他有k种不同类型的对象,每一种类型的有限重复数是n1,n2,n3,…nk。设S的大小为n=n1+n2+n3+…nk。则S的n排列数目为n!/(n1!n2!n3!…nk!)

 

用容斥原理

f(i)表示至少有i对颜色相同的球相邻的方案数

那么 答案=f(0)-f(1)+f(2)-f(3)……

 

如何求f(i) 

假设一共有c2对球颜色相同,c1个颜色唯一的球,c1=n-2*c2

要求至少有i对颜色相同的球相邻,就可以把每对看做1个球

所以相当于有c2-i对颜色相同的球,c1+i个颜色唯一的球,球的总数为2*(c2-i)+c1+i

根据有重复集合的排列公式 f(i)=C(c2,i) * (2*(c2-i)+c1+i)! / [2^(c2-i)]

 

组合数和逆元要递推,不然会超时

 

之前一直考虑先固定好颜色唯一的球或者颜色相同的球,再往里面差另一种,是不行的

因为可以插入颜色相同球相邻,后面通过插别的隔开他们

 

#include<map>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int mod=1e9+7;

#define N 1000001

int a[N];

int fac[N],inv[N];

int pow(int a,int b)
{
    int c=1;
    for(;b;a=1ll*a*a%mod,b>>=1)
        if(b&1) c=1ll*c*a%mod;
    return c;
}

int main()
{
    int n,x,c1=0,c2=0;
    scanf("%d",&n);
    if(!n)
    {
        printf("0");
        return 0; 
    }
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<n;++i) 
        if(a[i]==a[i+1]) c2++;
    c1=n-c2*2;
    fac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%mod;
    inv[1]=1;
    for(int i=2;i<=c2;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; 
    int inv2=pow(2,mod-2);
    int in=pow(inv2,c2); 
    int ans=1ll*fac[n]%mod*in%mod;
    int t=-1,c=1,tmp;
    for(int i=1;i<=c2;++i)
    {
        c=1ll*c*inv[i]%mod*(c2-i+1)%mod;
        in=2ll*in%mod;
        tmp=(1ll*c*fac[2*(c2-i)+c1+i]%mod*in%mod*t+mod)%mod;
        ans=(ans+tmp)%mod;
        t=-t; 
    }
    printf("%d",ans);
}

 

posted @ 2021-03-15 13:52  TRTTG  阅读(160)  评论(0编辑  收藏  举报