Description
求 \(n\) 元集的幂集有多少个子集的广义交大小为 \(k\)。
Solution
这里我们需要用到二项式反演
\[f(n)=\sum_{i=n}^m \binom i n g(i) \Leftrightarrow g(n)=\sum_{i=n}^m (-1)^{i-n} \binom i n f(i) \]
设 \(f(i)\) 表示交集元素至少有 \(i\) 个的方案数,设 \(g(i)\) 表示交集元素恰好为 \(i\) 个的方案数,于是有关系
\[f(k) = \sum_{i=k}^n g(i)\binom 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;
}