CSP-S 2024 擂台游戏 题解

  • 记只收到前 \(x\) 位选手的报名信息为情形 \(x\)
  • 记第 \(y\) 位选手为选手 \(y\)
  • \(k\) 是最小的非负整数使得 \(2^k \ge x\),记 \(f(x) = 2^k\)
  • 记选手 \(y\) 包含于情形 \(x\),当且仅当 \(y \le f(x)\)
  • 对于情形 \(x\),记选手 \(y\)正式选手,当且仅当 \(y \le x\),否则记选手 \(y\)补充选手
  • 记情形 \(x_1\) 与情形 \(x_2\) 相似,当且仅当 \(f(x_1) = f(x_2)\)

让我们先着眼于选手 \(y\),是否对包括他的情形 \(x\) 的答案产生贡献。
可以注意到:

  • \(x < y\) 时,选手 \(y\) 是补充选手,此时我们注意到:
    • 若有 \(x' < x\),且情形 \(x\) 与情形 \(x'\) 相似,那么,我们一定可以操作情形 \(x'\) 中编号在 \((x', x]\) 之间的补充选手的能力值等于原能力值,使其与情形 \(x\) 相同(简单来说,情形 \(x'\) 的所有情形包括情形 \(x\) 的所有情形,这个性质较为重要)。所以,若选手 \(y\) 能在情形 \(x\)\(x < y\))中取胜,那么他也可以在任意与情形 \(x\) 相似的情形 \(x'\)\(x' < x\))取胜。
    • 所以,能使选手 \(y\) 取胜的情形 \(x\)\(x < y\))是一个连续区间,且若区间非空,区间左端点一定为满足 \(f(x) = f(x')\) 的最小 \(x'\)
  • \(x \ge y\) 时,选手 \(y\) 是正式选手,此时我们同样注意到:
    • 类似的,若选手 \(y\) 不能在情形 \(x\)\(x \ge y\))中取胜,那么他也不可以在任意情形 \(x'\)\(x' > x\))取胜。
    • 所以,能使选手 \(y\) 取胜的情形 \(x\)\(x \ge y\))也是一个连续区间,且若区间非空,区间左端点一定为 \(y\)

综上,我们应着力于求出两个区间的右端点,分类讨论:

1. 选手 \(y\) 是补充选手

考虑选手 \(y\) 向上跳的过程:

  • 如果当前擂主是他,不用管,因为直接令选手 \(y\) 的能力值为无穷大便能保证他取胜。
  • 否则,我们把对局和选手视为结点,构建一颗满二叉树:
    • 若选手 \(y\) 作为当前对局的左儿子,那么他的对手 \(y'\) 一定也是补充选手(\(x < y < y'\)),设当前局势的轮数为 \(k\),保持其他补充选手的能力值不变,直接令选手 \(y'\) 的能力值为 \(k - 1\),可以证明:在这一对局之前,无论选手 \(y'\) 是否为擂主,选手 \(y'\) 一定能赢,而在这一对局中,选手 \(y'\) 一定输,所以 \(y\) 也一定赢,不用管。
    • 否则,若他的对手为正式选手,我们计算出正式选手的能力值最小值,若这个数小于 \(k\),就可以不用管了,否则我们考虑,若对于情形 \(x\),能使选手 \(y\) 的对手为补充选手,那么对于情形 \(x'\),也能做到这一点,证明是相似的。所以我们预处理出能使某颗子树 \(u\) 的胜者是补充选手的最大的情形 \(x\),记为 \(f_u\),将我们要求的右端点和 \(f_u\)\(\min\) 即可。

\(f_u\) 的预处理容易 \(O(n)\) 做到。所以类似的,从上往下递归也是容易的。

2. 选手 \(y\) 是正式选手

同样的:

  • 如果当前擂主是他,只需检查选手 \(y\) 的能力值是否大于等于当前轮数 \(k\)
  • 否则,同样构建一颗满二叉树:
    • 若选手 \(y\) 作为当前对局的右儿子,那么他的对手 \(y'\) 一定也是正式选手(\(x \ge y > y'\)),只需检查选手 \(y'\) 能否取胜。
    • 否则,我们的推导和选手 \(y\) 是补充选手的情况一致。

所以我们考虑从上往下递归,记录 \(y\) 作为擂主的最大轮数,也是容易的。
最终将右端点与相应左端点拼成一个区间并差分计算贡献即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
	int sig = 1; ll num = 0; char c = getchar();
	while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
	while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar();
	return num * sig;
}
void Write(ll x) {
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) Write(x / 10);
	putchar((x % 10) ^ 48);
}
const int N = 100005, inf = 1e9;
int n, m, a_[N], a[N << 1], c[N], p[N << 2], f[N << 2], g[N << 2], dep[N << 2];
ll ans[N];
bool d[N << 1];
void Init(int u) {
	if(u >= p[n]) {
		int x = u - p[n] + 1;
		f[u] = min(n, x - 1), g[u] = (x > n ? inf : a[x]);
		return ;
	}
	Init(u << 1), Init(u << 1 | 1);
	g[u] = g[(u << 1) ^ d[u] ^ (g[(u << 1) ^ d[u]] < dep[u])];
	if(!d[u]) f[u] = f[(u << 1) ^ (g[u << 1] < dep[u])];
	else f[u] = f[u << 1 | 1];
}
void Dfs(int u, int l, int r) {
	if(u >= p[n]) {
		int x = u - p[n] + 1; r = min(n, min(r, x - 1));
		if(l <= r) ans[l] += x, ans[r + 1] -= x;
		return ;
	}
	Dfs((u << 1) ^ d[u], l, r);
	if(d[u]) Dfs(u << 1, l, r);
	else {
		if(g[u << 1] < dep[u]) Dfs(u << 1 | 1, l, r);
		else Dfs(u << 1 | 1, l, min(r, f[u << 1]));
	}
}
void Dfs2(int u, int maxdep, int l, int r) {
	if(u >= p[n]) {
		int x = u - p[n] + 1; l = max(l, x), r = min(n, r);
		if((x > n || a[x] >= maxdep) && l <= r) ans[l] += x, ans[r + 1] -= x;
		return ;
	}
	Dfs2((u << 1) ^ d[u], max(maxdep, dep[u]), l, r);
	if(d[u]) {
		if(g[u << 1 | 1] < dep[u]) Dfs2(u << 1, maxdep, l, r);
		else Dfs2(u << 1, maxdep, l, min(r, f[u << 1 | 1]));
	}
	else if(g[u << 1] < dep[u]) Dfs2(u << 1 | 1, maxdep, l, r);
}
void Solve() {
	int i; Init(1);
	for(i = 1; i < p[n]; i *= 2) {
		if(d[i] || g[i << 1] < dep[i]) Dfs(i << 1 | 1, (1 << (dep[i] - 1)) + 1, 1 << dep[i]);
		Dfs2(i, 0, (1 << (dep[i] - 1)) + 1, 1 << dep[i]);
	}
	ans[1]++, ans[2]--;
	for(i = 1; i <= n; i++) ans[i] += ans[i - 1];
	ll res = 0;
	for(i = 1; i <= m; i++) res ^= 1ll * i * ans[c[i]];
	Write(res), putchar('\n');
}
int main() {
	int i, T; n = Read(), m = Read(), p[1] = 1;
	for(i = 1; i <= n; i++) a_[i] = Read();
	for(i = 1; i <= m; i++) c[i] = Read();
	for(i = 2; i <= 400000; i++) {
		p[i] = p[i - 1];
		while(p[i] < i) p[i] *= 2;
	}
	for(i = p[n] - 1; i; i--) dep[i] = dep[i << 1] + 1;
	for(i = p[n] - 1; i; i--) {
		char c = getchar();
		while(c != '0' && c != '1') c = getchar();
		d[p[i + 1] - i - 1 + p[i + 1] / 2] = c ^ 48;
	}
	T = Read();
	while(T--) {
		int x[4];
		for(i = 0; i < 4; i++) x[i] = Read();
		for(i = 1; i <= n; i++) a[i] = a_[i] ^ x[i % 4], ans[i] = 0;
		Solve();
	}
}
posted @ 2025-05-24 22:59  Include_Z_F_R_qwq  阅读(82)  评论(0)    收藏  举报