题解:P12393 「RiOI-6」flos

· · 题解

$\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})$。 ## 代码 ```cpp #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; } ```