Description
求 (n) 元集的幂集有多少个子集的广义交大小为 (k)。
Solution
这里我们需要用到二项式反演
[f(n)=sum_{i=n}^m inom i n g(i) Leftrightarrow g(n)=sum_{i=n}^m (-1)^{i-n} inom i n f(i)
]
设 (f(i)) 表示交集元素至少有 (i) 个的方案数,设 (g(i)) 表示交集元素恰好为 (i) 个的方案数,于是有关系
[f(k) = sum_{i=k}^n g(i)inom i k
]
根据二项式反演得
[g(k)=sum_{i=k}^n (-1)^{i-k} {i choose k}f(i)
]
考虑 (f(i)),我们先钦定 (i) 个元素为交集元素,包含这 (i) 个元素的集合有 (n-i) 个,可以选择任意个,但不能一个不选,于是
[f(i)={n choose i} (2^{2^{n-i}}-1)
]
于是得
[g(k)=sum_{i=k}^n (-1)^{i-k} {i choose k}{n choose i} (2^{2^{n-i}}-1)
]
后面的指数项可以根据费马小定理利用快速幂暴力计算,
设 (a_i={i choose k}, b_i={n choose i}),则 (a_k=1,b_n=1),且由于
[a_i=frac {i!}{k!(i-k)!} = frac {(i-1)!}{k!(i-k-1)!} frac i {i-k}=a_{i-1} frac i {i-k}
]
[b_i=frac{n!} {i!(n-i)!}=frac {n!} {(i+1)!(n-i-1)!} frac {i+1}{n-i}=b_{i+1} frac {i+1}{n-i}
]
都可以 (O(n)) 预处理,于是总时间复杂度 (O(n log n ))
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
int qpow(int p,int q,int mod) {
return (q&1?p:1) * (q?qpow(p*p%mod,q/2,mod):1) % mod;
}
int qpow(int p,int q) {
return qpow(p,q,mod);
}
int inv(int p) {
return qpow(p,mod-2);
}
const int N = 1000005;
int k,n,m,a[N],b[N];
void presolve() {
a[k]=1;
for(int i=k+1;i<=n;i++) a[i]=a[i-1]*i%mod*inv(i-k)%mod;
b[n]=1;
for(int i=n-1;i>=k;i--) b[i]=b[i+1]*(i+1)%mod*inv(n-i)%mod;
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>k;
presolve();
int ans=0;
for(int i=k;i<=n;i++) {
int tmp=a[i]*b[i]%mod*(qpow(2,qpow(2,n-i,mod-1))-1)%mod;
if((i-k)&1) ans-=tmp;
else ans+=tmp;
ans=(ans%mod+mod)%mod;
}
cout<<ans;
}