[BZOJ2839] 集合计数 (容斥+组合)

题目描述

一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1e9+7。

 

 

上式对吗?显然是不对的,以为它不仅统计了交集为k的而且统计了交集为k以上的。

所以我们要用容斥。

设g[i]为交集大于等于i的方案数,

那么:

  

用文字解释就是“任意取-g[1]+g[2]-g[3]...”;

注意一下的指数在处理的时候要%(mod-1)而不是mod(欧拉定理的推论)。

阶乘和逆元预处理即可。

 

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int N=1e6+5,mod=1e9+7;
ll ans,n,k,fac[N],inv[N];
ll poww(ll x,ll y,ll z)
{
        ll sum=1;
        while(y)
        {
                if(y&1) sum=(sum*x)%z;
                y>>=1;
                x=(x*x)%z;
        }
        return sum;
}
ll C(ll x,ll y)
{
        return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
        //freopen("1.in","r",stdin);
        //freopen("2.out","w",stdout);
        scanf("%lld%lld",&n,&k);
        fac[0]=1;
        for(ll i=1;i<=n;i++)
        {
                fac[i]=fac[i-1]*i%mod;
        }
        inv[n]=poww(fac[n],mod-2,mod);
        for(ll i=n-1;i>=0;i--)
        {
                inv[i]=inv[i+1]*(i+1)%mod;
        }
        for(ll i=n-k;i>=0;i--)
        {
                ll S=poww(2,n-k-i,mod-1);
                S=poww(2,S,mod);
                (ans+=((i&1)==1?-1:1)*C(n-k,i)*(S-1)%mod)%=mod;
                ans=(ans+mod)%mod;
        }
        (ans*=C(n,k))%=mod;
        printf("%lld",ans);
        return 0;
}
View Code

 

 

 

posted @ 2019-06-30 18:56  ATHOSD  阅读(97)  评论(0编辑  收藏  举报