QOJ #2550. Lion and Zebra 题解

Description

给定一棵包含 NN 个顶点的树。
在这棵树上进行一个“捉迷藏”游戏。游戏由若干回合组成。

在每一回合中,有两名玩家:

  • 狮子 —— 追捕方;
  • 斑马 —— 逃跑方。

在回合开始时,狮子和斑马分别站在两个不同的顶点上。狮子始终知道斑马的位置,并且以 每秒 1 条边 的速度追赶。斑马不知道狮子的位置,但始终知道 与狮子的距离。基于这些信息,斑马在每一秒可以做出如下两种选择之一:

  • 用 1 秒钟走到任意相邻顶点;
  • 停留在当前顶点 1 秒。

当狮子与斑马在同一个顶点相遇,或者在同一条边上相遇时,本回合结束。特别地,如果他们从相邻的两个顶点同时沿同一条边相向而行,那么会在出发后的 0.5 秒 相遇。

斑马的目标是:在所有可能的狮子初始位置中,选择自己的行动方式,使得最短相遇时间尽可能大化。

你被给定 QQ 个回合。在第 ii 个回合中:

  • 斑马从顶点 viv_i 出发;
  • 狮子与斑马的初始距离为 did_i

请你求出:在双方都采取最优策略时,第 ii 个回合的最短结束时间

n,q105n,q\leq 10^5

Solution

首先这题如果知道狮子的位置,则走法一定是往狮子的方向走,直到距离不超过 22,再反向走一个最长的路径跑路。

如果不知道狮子的位置,则每次就只能猜一个方向走,这个方向可能会很优也可能会很劣,考虑怎么最优化这个东西。

然后有个感觉是如果一次试探失败,即这个儿子 vv 的方向不是狮子的方向,后面不会回头。证明就考虑下一步如果回头,且最终还会回到 vv 的子树一定不优。如果最终不回 vv 的子树,则假设下一步会走到 ww,那么一开始就走 ww 一定更优。(感觉证明很感性,不保证严谨性)

所以策略一定是先一直往一个子树走,再根据情况往父亲方向跑路。

fuf_u 表示从根走到 uu,一路上都试探成功,且狮子在 uu 子树里后面的最大步数;dud_u 表示从根走到 uu 时与狮子的距离;disudis_u 表示 uu 子树到 uu 的最长距离。则有转移:

fu=max{maxvson(u){min(fv+1,du+disv+1)},disfau+du+1}f_u=\max\left\{\max_{v\in son(u)}{\left\{\min(f_v+1,d_u+dis_v+1)\right\}},dis_{fa\setminus u}+d_u+1\right\}

这个转移的含义是讨论第一步走的方向,如果是往儿子 vv 方向走且狮子在 vv 的子树里,贡献为 fv+1f_v+1;狮子不在 vv 的子树里,则由于不会走回头路,后面一定是往子树的最长路径走,也就是 du+disv+1d_u+dis_v+1,那么往 vv 走的贡献即为两者取 min。

如果往父亲走,则开始跑路,贡献为 disfau+du+1dis_{fa\setminus u}+d_u+1,就是走去掉 uu 子树的最长路径。

这么做是 O(nq)O(nq) 的,需要优化。

注意到往儿子走的转移 maxvson(u){min(fv+1,du+disv+1)}du+maxvson(u){du+disv+1}=du+disu\displaystyle\leq\max_{v\in son(u)}{\left\{\min(f_v+1,d_u+dis_v+1)\right\}}\leq d_u+\max_{v\in son(u)}{\left\{d_u+dis_v+1\right\}}=d_u+dis_u,则如果 uu 不是父亲 fafa 的长儿子,uu 往儿子方向走不如走回头路。这说明能往儿子走就一定会往长儿子方向走。

所以策略更新为每次往长儿子走,如果狮子不在长儿子子树里就一直走长儿子最优,否则继续走,直到与狮子距离不超过 22 时开始跑路。

容易发现这个策略会在狮子在根所在最长链上最劣,所以只需要考虑这种情况。

对于询问,容易发现根所在最长链的另一端一定是整棵树直径的一端点,预处理出直径的两端点。设 p=d12p=\left\lfloor\frac{d-1}{2}\right\rfloor,则走法一定是往这个端点先走 pp 步,再走反方向,且要求这么走的下一步子树里不能有狮子,搞个 set 维护即可。

时间复杂度:O((n+q)logn)O((n+q)\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n, q;
std::vector<int> G[kMaxN];

struct Tree {
int rt, mx[kMaxN], dep[kMaxN], fa[kMaxN][18];
std::set<int> st[kMaxN];
void dfs(int u, int _fa) {
mx[u] = 0, fa[u][0] = _fa;
for (int i = 1; i <= std::__lg(n); ++i)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (auto v : G[u]) {
if (v == _fa) continue;
dep[v] = dep[u] + 1;
dfs(v, u);
mx[u] = std::max(mx[u], mx[v] + 1);
st[u].emplace(mx[v] + 1);
}
}
int getfa(int x, int len) {
for (int i = std::__lg(n); ~i; --i)
if (len >> i & 1)
x = fa[x][i];
return x;
}
int get(int x, int len) {
auto it = st[x].lower_bound(len);
if (it != st[x].begin()) return *prev(it);
else return 0;
}
void prework(int _rt) { dfs(rt = _rt, 0); }
} t[2];

void dfs(int u, int fa, int *dis) {
dis[u] = dis[fa] + 1;
for (auto v : G[u]) {
if (v == fa) continue;
dfs(v, u, dis);
}
}

std::pair<int, int> getseg() {
static int dis[kMaxN] = {0};
dfs(1, 0, dis);
int s = std::max_element(dis + 1, dis + 1 + n) - dis;
dfs(s, 0, dis);
int t = std::max_element(dis + 1, dis + 1 + n) - dis;
return {s, t};
}

int solve(int x, int d) {
int o = (t[0].dep[x] < t[1].dep[x]), len = (d - 1) / 2;
int ret = 0;
if (len) {
int y = t[o].getfa(x, len - 1);
ret = t[o].mx[y] + 1;
}
x = t[o].getfa(x, len);
ret = std::max(ret, t[o].get(x, d - len));
return ret + d - len;
}

void dickdreamer() {
std::cin >> n >> q;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(v), G[v].emplace_back(u);
}
auto [a, b] = getseg();
t[0].prework(a), t[1].prework(b);
for (int i = 1; i <= q; ++i) {
int x, d;
std::cin >> x >> d;
std::cout << solve(x, d) << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

QOJ #2550. Lion and Zebra 题解
https://sobaliuziao.github.io/2025/08/20/post/7a90f139.html
作者
Egg_laying_master
发布于
2025年8月20日
许可协议