題目大意:一個有 \(N\) 個元素的集合有 \(2^N\) 個不同子集(包含空集),現在要在這 \(2^N\) 個集合中取出若干集合(至少一個),使得它們的交集的元素個數為 \(K\),求取法的方案數,答案模 \(10^9+7\)。
為表述方便,不妨設這 \(i\) 個元素分別為 \(1\sim n\)。
前置知識:二項式反演。
考慮設 \(g(x)\) 為我們選擇恰好 \(x\) 個數,要求選出若干個集合他們的交集裡是這 \(x\) 個數的方案數,這個東西由於是“恰好”不好計數,考慮設 \(f(x)\) 表示說我們欽定 \(x\) 個數他一定要在這若干個集合的交集中的方案數。
\(f(x)\) 是好求的,我們直接從集合中欽定 \(x\) 個數一定要在若干個集合的交集中(其他數在不在我不關心),然後考慮包含這 \(x\) 個數的集合數量,顯然除了這 \(x\) 個數必選以外,其餘的數可選可不選,所以這部分的數量是 \(2^{n-x}\) 的。然後對於這些包含這 \(x\) 個數的集合,對於每個集合來說我們也是可選可不選,所以最終這部分的答案是 \({n\choose x}\times(2^{2^{n-x}}-1)\),這部分的 \(-1\) 是因為要排除掉空集。
接下來考慮如果 我們已知 \(g\),如何用 \(g\) 表示出 \(f\)(以方便我們用二項式反演從 \(f\) 反推回 \(g\)),顯然\(f(x)=\sum_{i=x}^ng(i)\times{n\choose i}\),代表說我們對於每一個 \(i\ge x\),我們恰好選 \(i\) 個數的方案數之和就是欽定 \(x\) 個數的方案數。然後對這個式子用一下二項式反演:
最後用一遍擴充套件尤拉定理就做完了,時間複雜度 \(O(n\log n)\),如果預處理 \(2^{2^i}\) 可以做到 \(O(n)\)。
const int MAXN = 1e6 + 5, mod = 1e9 + 7;
int n, k, fac[MAXN], ifac[MAXN];
int quickpow(int a, int b, int p = mod) {
int ret = 1;
while (b) {
if (b & 1) ret = ret * a % p;
a = a * a % p; b >>= 1;
}
return ret;
}
int C(int n, int m) {
if (n < m) return 0;
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
void work() {
cin >> n >> k;
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
ifac[n] = quickpow(fac[n], mod - 2);
for (int i = n; i; i--) ifac[i - 1] = ifac[i] * i % mod;
int ans = 0, coff = 1;
for (int i = k; i <= n; ++i) {
ans = ans + coff * C(i, k) * C(n, i) % mod * quickpow(2, quickpow(2, n - i, mod - 1)) % mod;
ans = (ans % mod + mod) % mod;
coff = -coff;
}
cout << ans << endl;
}