[BZOJ2839] 集合计数

集合计数

题目描述

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

输入格式

一行两个整数$N$,$K$

输出格式

一行为答案。

样例

3 2
6

数据范围与提示

样例说明

假设原集合为{$A,B,C$}

则满足条件的方案为:{$AB,ABC$},{$AC,ABC$},{$BC,ABC$},{$AB$},{$AC$},{$BC$}

数据说明

对于$100\%$的数据,$1≤N≤1000000$;$0≤K≤N$;

 

首先先选出$k$个交集的元素,方案数为$C_n^k$;

然后已经选了$k$个元素,还剩下$n-k$个元素,那么共有$2^{n-k}$个集合可选。

那么我们从$2^{n-k}$个集合中选出i个集合的方案数为$C_{n-k}^i$。

总的方案数就是  $C_{2^{n-k}}^0+C_{2^{n-k}}^1+C_{2^{n-k}}^2+......+C_{2^{n-k}}^{2^{n-k}}$。

然后关键的就是一个公式   上面的式子就等于  $2^{2^{n-k}}$。

证明 参考 这里

其实也可以感性的理解一下,就是每个方案都有选和不选两种情况,乘法计数原理。

然后要想办法让剩下的方案里面没有交集才能满足条件。

 

关于容斥原理:

可以用递归的思想来证明,

首先我们要所有的加起来然后减去他们的交集,然后他们的交集中又有交集,所以可以接着减。

最后 $S=S_{所有}-S_{交集}$

          $=S_{所有}-(S_{交集}-S_{交集的交集})$

     $=......$

     $=S-(S-(S-(......)))$。

最后去掉括号就是式子中的 系数。

当然也有更严谨的证明,可以在网上搜索一下。

 

然后就是这个题,

至于为什么要用容斥,这是个问题。。。

不能用 总的方案数直接减去交集大于等于$1$的方案数。

  假设我们把这所有的方案分成几部分,有交集为$A$的,有交集为$B$的,有交集为$C$的,

  之后$A$和$B$的交集为$AB$,$A$和$C$的交集为$AC$,$B$的$C$的交集为$BC$,$A$、$B$、$C$的交集为$ABC$。

  那么也就很好理解这个题的容斥了。

如果我们用总方案数减去交集大于等于$1$的方案数,在维恩图中我们会看到多减了很多。

 

令$F_i=C_{n-k}^i \times (2^{2^{n-k-i}}-1)$。

这个含义就是我们已经选了$k$个元素作为交集,再选$i$个作为交集的方案数。

在这个式子中,肯定包含交集数$>i$的方案,所以说要用容斥。

我们要求的就是  所有交集数目$==0$的方案数。

我们只要用总方案数减去交集数$>=1$的方案的并集就可以。

 

最后那个$2^{2^{n-k-i}}$应该怎么算,可以用欧拉定理,算出$(2^{n-k-i})\%φ(mod)$。

然后算$2^{2^{n-k-i} \%φ(mod)} \%mod$。

#include<iostream>
#include<cstring>
#include<cstdio>
#define Reg register
#define mod 1000000007
#define Lucas(x,y) (jc[x]*ny[y]%mod*ny[x-y]%mod)
#define Ny(x) (Mi(x,mod-2,mod))
using namespace std;
long long ans,n,k,jc[1000050],ny[1000050];
long long Mi(long long x,long long y,long long p)
{
    long long ans=1,base=x;
    while(y)
    {
        if(y&1) ans=(ans*base)%p;
        base=(base*base)%p;
        y>>=1;
    }
    return ans;
}
long long Js(int x)
{
    long long l1=Mi(2,x,mod-1);
    long long ans=Mi(2,l1,mod);
    ans=(ans+mod-1)%mod;
    return ans;
}
void Jc(long long x)
{
    jc[0]=ny[0]=1;
    for(Reg int i=1;i<=x;++i)
    {
        jc[i]=(jc[i-1]*(i%mod))%mod;
        ny[i]=Ny(jc[i]);
    }
    return;
}
int main()
{
    scanf("%lld%lld",&n,&k);
    Jc(n);
    for(Reg int i=0,cur;i<=n-k;++i)
    {
        if((i&1)==0) cur=1;
        else cur=-1;
        long long q=(Lucas(n-k,i)*Js(n-i-k))%mod;
        ans=(ans+mod+q*cur)%mod;
    }
    ans=(ans*Lucas(n,k))%mod;
    printf("%lld",ans);
    return 0;
}
View Code

 

posted @ 2019-07-02 14:16  Milk_Feng  阅读(96)  评论(0编辑  收藏  举报