[bzoj2839]集合计数题解(容斥)

Posted wxy-max

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj2839]集合计数题解(容斥)相关的知识,希望对你有一定的参考价值。

题目描述

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

输入格式

一行两个整数(N,K)

输出格式

一行为答案。

对于(100)%的数据,(1≤N≤1000000)(0≤K≤N)

$ (TimeLimit:1s,MemoryLimit:128M)$

思路

我们首先确定我们交集所包含的(k)个元素,一共有(inom{n}{k})种方法。

假定我们当前确定的包含(k)个的元素的集合为(S)

那么包含(S)的子集相当于在除了(S)包括的元素中,选任意多元素的元素凑成的子集。

那么包含(S)的所有子集有

[sum_{i=0}^{n-k}inom{n-k}{i}=2^{n-k} ]

从中选任意多个集合相交共有

[sum_{i=1}^{2^{n-k}}inom{2^{n-k}}{i}=sum_{i=0}^{2^{n-k}}inom{2^{n-k}}{i}-inom{2^{n-k}}{0}=2^{2^{n-k}}-1 ]

种方案

但是这样算是所有交集大于等于(k)个元素的方案,即除了(S)还有其他的交集。

那么我们就要把其中所有交集大小大于(k)个元素的方案给减掉。

所有交集大小大于(k)个元素的方案等价于所有交集大小大于等于(k+1)个元素的方案。

我们当前的方案已经确定了包含(k)个元素的集合(S),那么我们只需要在除了(S)的元素中任选一个即可凑成一个包含(k+1)个元素的集合,设除了(S)以外的元素为(a),那么我们就可以用上面的方法算出交集至少为(S,a)的方案数。其中(a)共有(inom{n-k}{1})种选法。

那么是否我们直接减去

[inom{n-k}{1}(2^{2^{n-k-1}}-1) ]

就是交集恰好为(S)的方案数呢?

考虑下面的情况:

{(Sabcd,Sabcf)}

交集为(Sabc)

而当我们确定交集至少为(Sa,Sb,Sc)都可以凑成这种方案,也就会产生重复计数导致答案变小。

我们设(P_a)为交集除了包括(S),还需包括(a)的集合。

那么我们要求的是一个并集的关系,即

[left|igcup P ight| ]

我们考虑任意两个方案集合(P_a)(P_b)的交集

[left|P_a igcap P_b ight| ]

考虑其意义,即满足交集至少包含(S)(a)(b)的方案。

那么有

[left|P_a igcap P_b ight|=(2^{2^{n-k-2}}-1) ]

其方案集合交集的大小只与相交集合的个数有关,与具体是哪几个相交无关。

(P)集合一共有(n-k)

那么有

[left|igcup P ight|=sum_{i=1}^{n-k}inom{n-k}{i}(-1)^{j-1}(2^{2^{n-k-i}}-1) ]

那么确定交集为(k)个元素的方案数为

[egin{aligned} (2^{2^{n-k}}-1)-left|igcup P ight|&=(2^{2^{n-k}}-1)-sum_{i=1}^{n-k}inom{n-k}{i}(-1)^{j-1}(2^{2^{n-k-i}}-1)&=(2^{2^{n-k}}-1)+sum_{i=1}^{n-k}inom{n-k}{i}(-1)^{j}(2^{2^{n-k-i}}-1)&=sum_{i=0}^{n-k}inom{n-k}{i}(-1)^{j}(2^{2^{n-k-i}}-1) end{aligned} ]

那么答案为

[inom{n}{k} imes sum_{i=0}^{n-k}inom{n-k}{i}(-1)^{j}(2^{2^{n-k-i}}-1) ]

那么不同的两个交集(S)(A)之间的方案是否会有冲突?

那么显然是不会的,在我们上面的计算中,可以确保我们每种选(k)个元素的方案的交集一定是交集恰好为(S)(A)的。

代码

#include <iostream>

using namespace std;

const int N = 1e6 + 10,mod = 1e9 + 7;

long long fac[N],invfac[N],two[N];

int C(int n,int m){return n<m?0:(long long)fac[n]*invfac[m]%mod*invfac[n-m]%mod;}

inline void init(){
    fac[0]=invfac[0]=invfac[1]=1;
    for(int i=1;i<=N - 10;i++)fac[i]=(long long)fac[i-1]*i%mod;
    for(int i=2;i<=N - 10;i++)invfac[i]=(long long)(mod-mod/i)*invfac[mod%i]%mod;
    for(int i=2;i<=N - 10;i++)invfac[i]=(long long)invfac[i-1]*invfac[i]%mod;
    two[0] = 2;
    for (int i = 1; i <= N - 10; i++){
        two[i] = two[i - 1] * two[i - 1] % mod;
    }
}

int main(){
    init();
    int n,k;
    cin >> n >> k;
    long long ans = 0;
    for (int i = 0;i <= n - k; i++){
        if ((i % 2) == 0) ans = (ans + C(n-k,i) * (two[n - k - i] - 1) % mod) % mod;
        else ans = (ans - C(n-k,i) * (two[n - k - i] - 1) % mod + mod) % mod; 
    }
    cout << C(n,k) * ans % mod;
    return 0;
}

以上是关于[bzoj2839]集合计数题解(容斥)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2839: 集合计数 容斥+组合

BZOJ2839集合计数 组合数+容斥

bzoj 2839 集合计数 容斥广义容斥

二项式定理&反演证明&bzoj2839集合计数题解

bzoj 2839集合计数

bzoj2839 集合计数