一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。
额很好的容斥题
额考虑选择所有的交集为
这个很好理解但是剩下多少种集合:还有n-k个数
为额稍有常识就知道这等价于
然后从中选取集合这也是组合数
我们再次稍有常识一点
等价于
额但是这个东西是什么:什么也不是他并不表示不少于k的集合选择数
对于长度为j的在这个式子中是重复出现了次
反向证明:在一个长为j的串出现了多少次?上式为一个很好的证明
然后容斥!
你看:设
对于长度为i+1的,我们需要减去的倍这好理解
因为对于这种情况实际上和上面的是一个原理
这里陷入一个递归
发现:对于我们需要容斥掉但是对于有的被多减了所以乘上
然后一顿操作(展开)加上
所以答案为
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef int INT;
#define int long long
const int N=1000000+100;
int Quick_Pow(int x,int k,int mod){
int ret=1;
while(k){
if(k%2){
ret=ret*x%mod;
}
x=x*x%mod;
k/=2;
}
return ret;
}
int fac[N+1];
int inv[N+1];
void Pre(){
fac[0]=1;
for(int i=1;i<N;++i){
fac[i]=fac[i-1]*i%mod;
}
inv[N-1]=Quick_Pow(fac[N-1],mod-2,mod);
for(int i=N-2;i>=0;--i){
inv[i]=inv[i+1]*(i+1)%mod;
}
}
int C(int n,int m){
if(m>n)return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int n,k;
int F(int x){
return C(n,x)*((Quick_Pow(2,Quick_Pow(2,n-x,mod-1),mod)-1)%mod+mod)%mod;
}
int ans=0;
INT main(){
cin>>n>>k;
Pre();
int cur=-1;
for(int i=k;i<=n;++i){
cur=cur*-1;
ans=((ans+cur*F(i)*C(i,k)%mod)%mod+mod)%mod;
}
cout<<ans<<'\n';
}