[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)的所有子集有
个
从中选任意多个集合相交共有
种方案
但是这样算是所有交集大于等于(k)个元素的方案,即除了(S)还有其他的交集。
那么我们就要把其中所有交集大小大于(k)个元素的方案给减掉。
所有交集大小大于(k)个元素的方案等价于所有交集大小大于等于(k+1)个元素的方案。
我们当前的方案已经确定了包含(k)个元素的集合(S),那么我们只需要在除了(S)的元素中任选一个即可凑成一个包含(k+1)个元素的集合,设除了(S)以外的元素为(a),那么我们就可以用上面的方法算出交集至少为(S,a)的方案数。其中(a)共有(inom{n-k}{1})种选法。
那么是否我们直接减去
就是交集恰好为(S)的方案数呢?
考虑下面的情况:
{(Sabcd,Sabcf)}
交集为(Sabc)
而当我们确定交集至少为(Sa,Sb,Sc)都可以凑成这种方案,也就会产生重复计数导致答案变小。
我们设(P_a)为交集除了包括(S),还需包括(a)的集合。
那么我们要求的是一个并集的关系,即
我们考虑任意两个方案集合(P_a)与(P_b)的交集
考虑其意义,即满足交集至少包含(S),(a),(b)的方案。
那么有
其方案集合交集的大小只与相交集合的个数有关,与具体是哪几个相交无关。
而(P)集合一共有(n-k)个
那么有
那么确定交集为(k)个元素的方案数为
那么答案为
那么不同的两个交集(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]集合计数题解(容斥)的主要内容,如果未能解决你的问题,请参考以下文章