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;
}
posted @ 2021-10-15 21:45  Smallbasic  阅读(122)  评论(0)    收藏  举报