题意:一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。
明显容斥啦。
应该是选择k个-选择k+1个+选择k+2…..
那么我们设f[i]表示已经选择k个作为交集,剩下n-k个如何选择的方案。
那么明显有f[i]=2^{2^(n-i)}-1,由于已经选择了k个,剩下n-k个集合可以选或者不选,但是不能全部不选所以-1。
那么剩下n-k如何选择已经求出,问题就是前面k个了,方案为C(n,i)*C(i,k)(k<=i<=n).
上面那个也好理解,具体来说,就是我现在只有可能选择i个,在这i个里面选择k个作为交集的方案。
注意求f的时候,幂次的mo是1e9+7-1,费马小定理。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
const int mo=1e9+7;
typedef long long ll;
ll fac[N],inv[N];
ll f[N],g[N];
int n,k;
inline ll C(int n,int m)
{
return fac[n]*inv[m]%mo*inv[n-m]%mo;
}
inline ll pow(ll a,ll b,ll mo)
{
ll ret=1;
while(b)
{
if (b&1)ret=ret*a%mo;
a=a*a%mo;
b>>=1;
}
return ret;
}
int main()
{
scanf("%d%d",&n,&k);
fac[0]=1;
fo(i,1,n)fac[i]=fac[i-1]*i%mo;
inv[0]=1;
fo(i,0,n)inv[i]=pow(fac[i],mo-2,mo)%mo;
g[n]=2,f[n]=1;
fd(i,n-1,0)
{
g[i]=pow(2,pow(2,n-i,mo-1),mo);
f[i]=(g[i]-1)%mo;
}
ll ans=0,c=-1;
fo(i,k,n)
{
c*=-1;
if (c<0)
ans=(ans-f[i]*C(i,k)%mo*C(n,i)%mo+mo)%mo;
else ans=(ans+f[i]*C(i,k)%mo*C(n,i)%mo)%mo;
}
printf("%lld\n",(ans+mo)%mo);
}