【思路要点】
- 补集转化,对于每个询问 S S S ,考虑求不能满足条件的方案数 q u e r y ( S ) query(S) query(S) 。
- 记 c n t S = ∑ i = 1 N [ a i ∪ S = ∅ ] cnt_S=\sum_{i=1}^{N}[a_i\cup S=\empty] cntS=∑i=1N[ai∪S=∅] ,即不能满足 s s s 中任何一个条件的 a i a_i ai 的个数。
- 则由容斥原理,有 q u e r y ( S ) = ∑ T ⊆ S , T ≠ ∅ ( − 1 ) ∣ T ∣ − 1 ( c n t T k ) query(S)=\sum_{T\subseteq S,T\ne\empty}(-1)^{|T|-1}\binom{cnt_T}{k} query(S)=T⊆S,T̸=∅∑(−1)∣T∣−1(kcntT)
- 两个求和符号的部分均可以用高维前缀和进行优化。
- 时间复杂度 O ( 20 × 2 20 + N + Q ) O(20\times2^{20}+N+Q) O(20×220+N+Q) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int N = 20; const int MAXN = 5e5 + 5; const int MAXS = 1 << N; const int P = 1e9 + 7; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int fac[MAXN], inv[MAXN]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } int binom(int x, int y) { if (y > x) return 0; else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P; } void init(int n) { fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % P; inv[n] = power(fac[n], P - 2); for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1ll) % P; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } int n, k, q, cnt[MAXS], bits[MAXS], ans[MAXS]; int gets() { char s[25]; int ans = 0; scanf("\n%s", s + 1); for (int i = 1; i <= N; i++) ans = ans * 2 + (s[i] == '1'); return ans; } int main() { freopen("swords.in", "r", stdin); freopen("swords.out", "w", stdout); read(n), read(k), read(q), init(n); for (int i = 1; i <= n; i++) { int x = gets(); cnt[(MAXS - 1) ^ x]++; } for (int i = 1; i < MAXS; i++) bits[i] = bits[i - (i & -i)] + 1; for (int len = 2; len <= MAXS; len <<= 1) { for (int i = 0; i < MAXS; i += len) for (int j = i, k = i + len / 2; k < i + len; j++, k++) cnt[j] += cnt[k]; } for (int i = 1; i < MAXS; i++) if (bits[i] & 1) ans[i] = binom(cnt[i], k); else ans[i] = (P - binom(cnt[i], k)) % P; for (int len = 2; len <= MAXS; len <<= 1) { for (int i = 0; i < MAXS; i += len) for (int j = i, k = i + len / 2; k < i + len; j++, k++) update(ans[k], ans[j]); } int tot = binom(n, k); for (int i = 1; i <= q; i++) { int x = gets(); writeln((tot - ans[x] + P) % P); } return 0; }