- 一个有N个元素的集合有
2N个不同子集(包含空集),现在要在这
2N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为
K,求取法的方案数,答案模1000000007。
- 交集元素个数至少为
K的方案数好求,恰好元素个数为
K的方案数不好求。
- 设
f[k]表示至少为
K的方案数,
g[k]表示恰好为
K的方案数。
-
f[k]=∑i=knCik∗g[i]。二项式反演一下,
g[k]=∑i=kn(−1)i−kCik∗f[i]。
- 现在只需要考虑
f怎么求。
f[k]=Cnk∗(22n−k−1)。
-
22n−k不好直接求,根据欧拉定理的推论,把幂先模
φ(mod)即
mod−1。
-
O(n)预处理一下
f数组,
O(n)求答案。
Coding
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
int n,k;
ll inv[N],jie[N],njie[N],f[N],ans;
ll power(ll a,ll b,ll p){
ll res=1%p;
for(;b;b>>=1){
if(b&1) res=res*a%p;
a=a*a%p;
}
return res;
}
int main(){
scanf("%d%d",&n,&k);inv[1]=1;
for(int i=2;i<=n;++i){
ll x=-mod/i*inv[mod%i]%mod;
x=(x+mod)%mod;inv[i]=x;
}jie[1]=njie[1]=1;jie[0]=njie[0]=1;
for(int i=2;i<=n;++i) jie[i]=jie[i-1]*i%mod,njie[i]=njie[i-1]*inv[i]%mod;
for(int i=0;i<=n;++i){
ll c=jie[n]*njie[i]%mod*njie[n-i]%mod;
ll temp=power(2,n-i,mod-1);
f[i]=c*(power(2,temp,mod)-1)%mod;
f[i]=(f[i]+mod)%mod;
}
for(int i=k;i<=n;++i){
ll c=jie[i]*njie[k]%mod*njie[i-k]%mod;
c=c*f[i]%mod;
if((i-k)%2) ans=(ans-c)%mod,ans=(ans+mod)%mod;
else ans=(ans+c)%mod;
}
cout<<ans<<endl;
return 0;
}