题解:P12393 「RiOI-6」flos
\(\text{Upd 2025/5/2}\):修正一些笔误,简化了做法。
\(\text{Upd 2025/5/16}\):走直径的做法假了,改为正确做法。
题意
给定一棵以 \(1\) 为根的树。一个人从 \(x\) 出发,有 \(t\) 秒的时间,往远离根方向每走一个单位就会耗费 \(1\) 秒,往靠近根方向走不耗费时间。\(q\) 次询问 \(x,t\),求能走到的最远的距离。\(1\leq n,q\leq 2\times 10^5\),强制在线。
题解
之前走直径的做法假完了,补一发正解。
我们的策略必然是先向上走一段,然后一直向下走。首先考虑一直向上走和一直向下走的情况,此时答案为 \(\max(dep_x-1,\min(f_x,t))\),其中 \(dep_1=1\),\(f_u\) 表示从点 \(u\) 向下走能走到的最远距离。
接下来令 \(g_u\) 表示从 \(fa_u\) 开始不经过 \(u\) 能走到的最远距离,则答案就是
\[\max_{fa_u\in anc(x)}\{dep_x-dep_{fa_u}+\min(t,g_u)\}
\]
拆 \(\min\):
- \(g_u\geq t\):我们只需取深度最小的 \(u\)。预处理 \(G_u\) 表示 \(u\) 到根节点的链上的 \(g\) 的最大值,树上倍增跳出深度最小的点 \(p\) 即可。
- \(g_u<t\):显然 \(u\) 必须是 \(v\) 的祖先,否则必然不优,于是预处理 \(h_u\) 表示 \(u\) 到根节点的链上的 \(g_v-dep_{fa_v}\) 的最大值即可。
时间复杂度 \(\mathcal{O}(n\log{n})\)。
代码
#include <iostream>
#include <cstring>
using namespace std;
#define lowbit(x) ((x) & -(x))
#define chk_min(x, v) (x) = min((x), (v))
#define chk_max(x, v) (x) = max((x), (v))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5, LGN = 18 + 5, INF = 1e9;
int n, q, d, ans, f[N][2], g[N], mxg[N], h[N];
int lg2[N], dep[N], fa[LGN][N];
struct AdjList {
int tot, head[N], nxt[N << 1], to[N << 1];
inline void init(int n) {
tot = 0;
for (int i = 1; i <= n; ++i) head[i] = -1;
}
inline void ins(int x, int y) { to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }
} tree;
inline void init(int n) {
for (int i = 2; i <= n; ++i) lg2[i] = lg2[i >> 1] + 1;
for (int i = 1; i <= n; ++i) h[i] = -INF;
tree.init(n);
}
inline void dfs1(int x) {
for (int i = 1; i <= lg2[dep[x]]; ++i) fa[i][x] = fa[i - 1][fa[i - 1][x]];
for (int i = tree.head[x]; ~i; i = tree.nxt[i]) {
int y = tree.to[i];
if (y == fa[0][x]) continue;
fa[0][y] = x, dep[y] = dep[x] + 1, dfs1(y);
if (f[y][0] + 1 > f[x][0]) f[x][1] = f[x][0], f[x][0] = f[y][0] + 1;
else chk_max(f[x][1], f[y][0] + 1);
}
}
inline void dfs2(int x) {
for (int i = tree.head[x]; ~i; i = tree.nxt[i]) {
int y = tree.to[i];
if (y == fa[0][x]) continue;
g[y] = f[x][(f[y][0] + 1) == f[x][0]], mxg[y] = max(mxg[x], g[y]);
h[y] = max(h[x], g[y] - dep[x]);
dfs2(y);
}
}
inline int query(int x, int t) {
for (int i = lg2[dep[x]]; i >= 0; --i)
if (mxg[fa[i][x]] >= t) x = fa[i][x];
return fa[0][x];
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q >> d, init(n);
for (int i = 1, u, v; i < n; ++i) cin >> u >> v, tree.ins(u, v), tree.ins(v, u);
dep[1] = 1, dfs1(1), dfs2(1);
while (q--) {
int x, t; cin >> x >> t;
if (d) x ^= ans, t ^= ans;
if (x == 1) ans = min(t, f[x][0]);
else if (!t) ans = dep[x] - 1;
else if (mxg[x] < t) ans = max(min(t, f[x][0]), dep[x] + h[x]);
else {
int y = query(x, t);
ans = max(min(t, f[x][0]), dep[x] - 1);
if (y) chk_max(ans, dep[x] + max(h[y], t - dep[y]));
}
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号