Description
一个有N个元素的集合有2N个不同子集(包含空集),现在要在这2N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)
Solution
这个容斥就很简单了
考虑设g[k]表示至少k个相同的答案,f[k]表示恰好k个相同的答案
显然有
g
k
=
∑
i
=
k
n
(
i
k
)
f
k
g_k=\sum\limits_{i=k}^{n}{\binom{i}{k}f_k}
gk=i=k∑n(ki)fk
考虑g[]的含义,不难得到
g
k
=
(
n
k
)
⋅
(
2
2
n
−
k
−
1
)
g_k=\binom{n}{k}\cdot\left(2^{2^{n-k}}-1\right)
gk=(kn)⋅(22n−k−1)
然后有一个神奇的变换就是
f
k
=
∑
i
=
k
n
(
−
1
)
i
−
k
(
i
k
)
g
k
f_k=\sum\limits_{i=k}^n{{(-1)}^{i-k}\binom{i}{k}g_k}
fk=i=k∑n(−1)i−k(ki)gk
这是一个反演,证明的话带入一下就可以了。。
于是我们就可以
O
(
n
)
O(n)
O(n)求恰好k的答案了
没有交,本地拍了一下似乎没毛病
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int MOD=1000000007;
const int N=200005;
LL fac[N],inv[N],f[N],g[N];
LL C(int n,int m) {
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
LL ksm(LL x,LL dep,int MOD) {
LL res=1;
for (;dep;dep>>=1) {
(dep&1)?(res=res*x%MOD):0;
x=x*x%MOD;
}
return res;
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
fac[0]=fac[1]=1; rep(i,2,N-1) fac[i]=fac[i-1]*i%MOD;
inv[0]=inv[1]=1; rep(i,2,N-1) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
rep(i,2,N-1) inv[i]=inv[i-1]*inv[i]%MOD;
int n,k; scanf("%d%d",&n,&k);
rep(i,1,n) g[i]=C(n,i)*(ksm(2,ksm(2,n-i,MOD-1),MOD)-1)%MOD;
rep(j,k,n) {
LL tmp=C(j,k)*g[j]%MOD;
if ((j-k)&1) f[k]=(f[k]+MOD-tmp)%MOD;
else f[k]=(f[k]+tmp)%MOD;
}
printf("%lld", f[k]);
return 0;
}