【问题描述】
一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数哦)
【数据范围】
1<=N<=1000000;0<=K<=N。
被虐傻了。。。。。
想了2小时,一直把公式套错。。。。
不多说,说多了全是泪
假设我们已经确定了k个元素,求最后并集为此k个元素的方案。很明显不同的k个元素是彼此独立的,所以直接假设确定了k个元素,求方案乘以组合数就行了。
至此题目相当于求n-k个元素互不相交的方案(其实这还是我打表发现的)
下面就是蒟蒻逗比的地方了:怎么求(n-k,0)的答案!之前两小时把公式套错了,本来应该第一个想到的容斥直接被我忽略掉,悲催了。。。。
容斥是正解!
设m=n-k,那么至少有0个的方案为C(m,m)*2^2^m ,至少有1个的方案为C(m,m-1)*2^2^(m-1),至少有2个的方案为C(m,m-2)*2^2^(m-2)……
容斥很明显了。
(我怎么这么逗比)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define C(n,m) (jc[n]*ny[m]%Mod*ny[(n)-(m)]%Mod)
typedef long long LL;
const int Mod=(1e9)+7;
int n,m,i,j,tot,prime[1000005];
LL ans,tmp,Cn,jc[1000005],ny[1000005],pw[1000005];
bool check[1000005];
LL qck(LL a,int b){
LL ret=1;
for (;b>0;b>>=1){
if (b&1) ret=ret*a%Mod;
a=a*a%Mod;
}
return ret;
}
void init(){
for (i=2;i<1000000;i++){
if (check[i]==0){
prime[++tot]=i;
ny[i]=qck(i,Mod-2);
}
for (j=1;j<=tot;j++){
if (i*prime[j]>=1000000) break;
check[i*prime[j]]=1;
ny[i*prime[j]]=ny[i]*ny[prime[j]]%Mod;
if (i%prime[j]==0) break;
}
}
jc[0]=1; jc[1]=1;
ny[0]=1; ny[1]=1;
pw[0]=2; pw[1]=4;
for (i=2;i<=1000000;i++){
jc[i]=jc[i-1]*(LL)i%Mod;
ny[i]=ny[i-1]*ny[i]%Mod;
pw[i]=pw[i-1]*pw[i-1]%Mod;
}
}
int main(){
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
init();
scanf("%d%d",&n,&m);
for (i=n-m;i>=0;i--){
Cn=C(n-m,i);
if ((n-m-i)&1) tmp=(tmp-pw[i]*Cn%Mod+Mod)%Mod;
else tmp=(tmp+pw[i]*Cn)%Mod;
}
ans=tmp*C(n,m)%Mod;
printf("%I64d\n",ans);
return 0;
}