JOI2018 Snake Escaping

发布时间 2023-07-21 08:29:57作者: Ender_32k

好神奇的做法,我称其为猪猪(猪笼原理)分治。

\(0,1,?\) 的个数分别为 \(a,b,c\)。有一个显然的 \(O(2^c)\) 做法,对每个 \(?\) 枚举其为 \(0/1\) 即可。

然后我们考虑只有 \(?,1\) 的情况,把所有 \(?\) 当成 \(0\),答案就是一个超集和;同理,对于只有 \(?,0\) 的情况,把所有 \(?\) 当成 \(1\),答案就是一个子集和。两者都可以用 FWT 预处理出来。

于是我们考虑把 \(0\)\(1\) 的其中一个用 \(?\) 来替换。比方说 \(0\) 换成 \(?\),根据容斥,应该减去 \(?\)\(1\) 的情况。这同样可以轻易地解决,于是我们得到了 \(O(2^a)\)\(O(2^b)\) 的做法。

综合一下,如果我们每次只选 \(a,b,c\) 中较小的做法来做,复杂度为 \(O(2^{\min\{a,b,c\}})\)。根据猪笼(抽屉)原理,\(\min\{a,b,c\}\le \left\lfloor\dfrac{L}{3}\right\rfloor\),所以总复杂度为 \(O(n2^n+q2^{L/3})\),能过。

int dfs0(int st, int dep) {
	if (dep == n) return g[st];
	if (s[dep] == '0') return dfs0(st << 1, dep + 1) - dfs0(st << 1 | 1, dep + 1);
	if (s[dep] == '?') return dfs0(st << 1, dep + 1);
	return dfs0(st << 1 | 1, dep + 1);
}

int dfs1(int st, int dep) {
	if (dep == n) return f[st];
	if (s[dep] == '1') return dfs1(st << 1 | 1, dep + 1) - dfs1(st << 1, dep + 1);
	if (s[dep] == '?') return dfs1(st << 1 | 1, dep + 1);
	return dfs1(st << 1, dep + 1);
}

int dfs2(int st, int dep) {
	if (dep == n) return a[st];
	if (s[dep] == '?') return dfs2(st << 1, dep + 1) + dfs2(st << 1 | 1, dep + 1);
	return dfs2(st << 1 | (s[dep] - '0'), dep + 1);
}

int main() {
	n = read(), q = read(), S = (1 << n);
	for (int i = 0; i < S; i++) scanf("%1d", &a[i]), f[i] = g[i] = a[i];
	for (int o = 2, k = 1; o <= S; o <<= 1, k <<= 1)
		for (int i = 0; i < S; i += o)
			for (int j = 0; j < k; j++)
				f[i + j + k] += f[i + j], g[i + j] += g[i + j + k];
	while (q--) {
		scanf("%s", s);
		int a = 0, b = 0, c = 0;
		for (int i = 0; i < n; i++) 
			a += (s[i] == '0'), b += (s[i] == '1'), c += (s[i] == '?');
		if (c <= n / 3) write(dfs2(0, 0));
		else if (a <= n / 3) write(dfs0(0, 0));
		else write(dfs1(0, 0));
		puts("");
	}
	return 0;
}