题目链接:https://darkbzoj.tk/problem/2839
题意:
一个有N个元素的集合有
2
N
个
不
同
子
集
(
包
含
空
集
)
2^{N个不同子集(包含空集)}
2N个不同子集(包含空集),现在要在这
2
N
2^N
2N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数
题解:
设
f
[
K
]
f[K]
f[K]表示交集大小
≥
K
\ge K
≥K的方案数,则
f
[
K
]
=
C
(
n
,
k
)
∗
(
2
2
n
−
i
−
1
)
f[K] = C(n,k)*(2^{2^{n-i}}-1)
f[K]=C(n,k)∗(22n−i−1) (考虑钦定 k 个,剩下的随便选,有
2
n
−
i
2^{n-i}
2n−i个集合,共
2
2
n
−
i
2^{2^{n-i}}
22n−i种 且不能不选)
设
g
[
K
]
g[K]
g[K]表示交集恰好为
K
K
K的方案数,则
f
[
K
]
=
∑
i
=
K
n
C
(
n
,
i
)
g
[
i
]
f[K]=\sum_{i=K}^nC(n,i)g[i]
f[K]=∑i=KnC(n,i)g[i]
根据二项式反演,
g
[
K
]
=
∑
i
=
k
n
(
−
1
)
i
−
k
C
i
k
f
(
i
)
g[K] = \sum_{i=k}^n(-1)^{i-k}C_i^kf(i)
g[K]=∑i=kn(−1)i−kCikf(i)
然后
2
2
X
2^{2^X}
22X用费马小定理,等于
2
(
2
x
)
%
(
m
o
d
−
1
)
2^{(2^x) \% (mod-1)}
2(2x)%(mod−1)两次快速幂即可
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9, maxn=1e6+5, mod=1e9+7;
int n,k;
int neg(int x){if(x&1)return -1;return 1;}
int f[maxn],fac[maxn],inv[maxn];
int pw(int x,int y,int md){
if(y == 0)return 1;if(y == 1)return x;
int mid=pw(x,y>>1,md);
if(y&1)return 1ll*mid*mid%md*x%md;
return 1ll*mid*mid%md;
}
int C(int x,int y){
if(x<y)return 0;
return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main(){
fac[0] = inv[0] = 1;
for(int i=1;i<=maxn-5;i++)fac[i] = 1ll*fac[i-1]*i%mod;
inv[maxn-5] = pw(fac[maxn-5],mod-2,mod);
for(int i=maxn-6;i>=1;i--)inv[i] = 1ll*inv[i+1]*(i+1)%mod;
scanf("%d%d",&n,&k);
for(int i=0;i<=n;i++){
f[i] = 1ll*C(n,i)*(pw(2,pw(2,n-i,mod-1),mod)-1)%mod;
}
LL ans = 0;
for(int i=k;i<=n;i++){
ans = (ans + 1ll*neg(i+k)*C(i,k)%mod*f[i]%mod)%mod;
ans = (ans+mod)%mod;
// printf("%lld\n",ans);getchar();
}
printf("%lld\n",ans);
return 0;
}