题解:P14653 [集训队互测 2025] 你的互相追逐的头
Priestess_SLG · · 题解
可能是对官方题解的复述。
求出所有初始有人的点组成的虚树,记其任意直径端点为
进一步观察后可以发现每个人在追上后继结点前必然走的是树上的一段连续简单路径,追上之后就继续沿着后继的轨迹移动。考虑维护
询问判掉
实现上为了方便处理路径中点的情况把一条边
namespace lowspeed_song {
mt19937_64 mt(time(0));
inline void init() {
}
pair<int, int> e[N];
vector<int> adj[N];
int dep[N], st[N][20];
inline void dfs(int u, int fa) {
st[u][0] = fa, dep[u] = dep[fa] + 1;
for (int &v : adj[u]) if (v != fa) dfs(v, u);
}
inline int lca(int a, int b) {
if (dep[a] < dep[b]) swap(a, b);
for (int i = 19; ~i; --i) if (dep[a] - dep[b] >> i & 1) a = st[a][i];
if (a == b) return a;
for (int i = 19; ~i; --i) if (st[a][i] != st[b][i]) a = st[a][i], b = st[b][i];
return st[a][0];
}
inline int dist(int a, int b) {
return dep[a] + dep[b] - 2 * dep[lca(a, b)];
}
inline int kth(int x, int k) {
for (int i = 19; ~i; --i) if (k >> i & 1) x = st[x][i];
return x;
}
inline int get(int u, int v, int dist) {
int LCA = lca(u, v);
if (dist <= dep[u] - dep[LCA]) return kth(u, dist);
return kth(v, dep[u] + dep[v] - 2 * dep[LCA] - dist);
}
int st_ne[N][20], st_mx[N][20];
inline void sol([[maybe_unused]]int __testcase_id, [[maybe_unused]]int __testcase_sum) {
int n, m; cin >> n >> m;
for (int i = 1; i < n; ++i) {
int a, b; cin >> a >> b;
if (a > b) swap(a, b);
e[i] = {a, b};
adj[a].emplace_back(i + n);
adj[i + n].emplace_back(a);
adj[b].emplace_back(i + n);
adj[i + n].emplace_back(b);
} dfs(1, 0);
for (int i = 1; i < 20; ++i)
for (int j = 1; j <= n + n; ++j) st[j][i] = st[st[j][i - 1]][i - 1];
while (m--) {
int k, q; cin >> k >> q;
vector<int> x(k), tim(k, 0), pos(k, 0);
for (int &i : x) cin >> i;
int a = 0, mx = 0;
for (int i = 0; i < k; ++i) {
int d = dist(x[0], x[i]);
if (mx < d) mx = d, a = i;
} int b = a; mx = 0;
for (int i = 0; i < k; ++i) {
int d = dist(x[a], x[i]);
if (mx < d) mx = d, b = i;
} int half = dist(x[a], x[b]) / 2;
int center = get(x[a], x[b], half);
tim[a] = half, pos[a] = center;
vector<pair<int, int>> stk = {
{center, half},
{x[a], 0}
};
for (int o = 1; o < k; ++o) {
int i = (a - o + k) % k;
while (1) {
auto [A, timA] = stk.back();
auto [B, timB] = stk[stk.size() - 2];
if (dist(x[i], B) > timB) stk.pop_back();
else {
tim[i] = (timA + dist(x[i], A)) / 2, pos[i] = get(A, B, tim[i] - timA);
stk.back() = {pos[i], tim[i]}, stk.emplace_back(x[i], 0);
break;
}
}
} for (int i = 0; i < k; ++i) {
st_mx[i][0] = tim[i];
st_ne[i][0] = (i + 1) % k;
} for (int i = 1; i < 20; ++i)
for (int j = 0; j < k; ++j) {
st_ne[j][i] = st_ne[st_ne[j][i - 1]][i - 1];
st_mx[j][i] = max(st_mx[j][i - 1], st_mx[st_ne[j][i - 1]][i - 1]);
}
while (q--) {
int p, t; cin >> p >> t;
if (2 * t >= half) {
if (center <= n) cout << center << ' ' << center << '\n';
else {
auto [u, v] = e[center - n];
cout << u << ' ' << v << '\n';
}
} else {
--p;
for (int j = 19; ~j; --j)
if (st_mx[p][j] <= 2 * t) p = st_ne[p][j];
int id = get(x[p], pos[p], 2 * t);
if (id <= n) cout << id << ' ' << id << '\n';
else {
auto [u, v] = e[id - n];
cout << u << ' ' << v << '\n';
}
}
}
}
}
} // namespace lowspeed_song