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();
}
}

浙公网安备 33010602011771号