kor
Mr.Hu 觉得在学习过程中,需要举一反三,做一题要理解透,然后遇到相似的问题时能类似地转化。所以想了一道和以前类似的题目,相信聪明如你,肯定能轻而易举地解决。
Mr.Hu 会给你 \(n\) 个非负整数,然后从中选 \(k\) 个出来,然后把这 \(k\) 个数按位或起来,Mr.Hu 想知道有多少种选法,使得或起来的结果为 \(r\)。
题解:
其实是有套路的。我们令 \(g[x]\) 表示 \(r=x\) 时的答案,\(f[x]\) 表示给你的整数中有多少个是 \(x\) 的子集(即或上 \(x\) 仍等于 \(x\)),由于这些整数中随便选 \(k\) 个出来或上都是 \(x\) 的子集,我们有:
\[\binom {f[S]} k =\sum_{T \subseteq S} g[T]
\]
子集反演一下得到:
\[g[S]=\sum_{T\subseteq S} (-1)^{|S|-|T|}\binom {f[T]} k
\]
\(g[S]\) 可以高维前缀和/FWT预处理。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
int f[1 << 24], n, kk, r, a, T, bt[1 << 24], fac[N], ifac[N];
inline int power(int a, int b) {
int t = 1, y = a, k = b;
while (k) {
if (k & 1) t = (1ll * t * y) % mod;
y = (1ll * y * y) % mod; k >>= 1;
} return t;
}
inline int read() {
register int s = 0; register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
inline int C(int n, int m) {
if (n < m) return 0;
return (1ll * fac[n] * ((1ll * ifac[m] * ifac[n - m]) % mod)) % mod;
}
int main() {
freopen("kor.in", "r", stdin); freopen("kor.out", "w", stdout);
T = read(); int n = (1 << 24) - 1; bt[0] = 0;
fac[0] = 1; for (int i = 1; i <= N - 5; ++i) fac[i] = (1ll * fac[i - 1] * i) % mod;
ifac[N - 5] = power(fac[N - 5], mod - 2);
for (int i = N - 6; ~i; --i) ifac[i] = (1ll * ifac[i + 1] * (i + 1)) % mod;
for (int i = 1; i <= n; ++i) bt[i] = bt[i >> 1] + (i & 1);
while (T--) {
memset(f, 0, sizeof f);
int nn = read(); kk = read(); r = read();
for (int i = 1; i <= nn; ++i) {
a = read();
if ((a | r) == r) ++f[a];
}
if (kk == 1) { printf("%d\n", f[r]); continue; }
for (int i = 2, j = 1; i <= n; i <<= 1, j <<= 1)
for (int k = 0; k < n; k += i)
for (int l = 0; l < j; ++l)
f[j + k + l] += f[k + l];
int ans = 0;
for (int S = r; S; S = r & (S - 1)) {
int t = bt[r] - bt[S];
if (t < 0) t += (1 << 20);
if (t & 1) {
ans = ans - C(f[S], kk);
if (ans < 0) ans += mod;
} else {
ans += C(f[S], kk);
if (ans >= mod) ans -= mod;
}
} printf("%d\n", ans);
}
return 0;
}