abc 267 f 题解
abc 267 f
题意
给定一个有 \(N\) 个结点的树,结点分别编号为 \(1, 2, \dots, N\),第 \(i\) 条边 \((1 \le i \le N - 1)\) 连接点 \(A_i, B_i\)。
树上的两个结点 \(u, v\) 之间的距离定义为从 \(u\) 到 \(v\) 的最短路径所经过的边的数量。
给定 \(Q\) 次询问,第 \(i\) 次询问给出 \(U_i, K_i\),请你输出任意一个与 \(U_i\) 距离为 \(K_i\) 的点的编号。如果不存在,输出 -1
。
思路
在线
首先,我们先思考这样一个问题,在什么情况下,不存在与点 \(u\) 距离为 \(k\) 的点?
当从 \(u\) 出发的最长路小于 \(k\) 时,就说明不存在这样的点。
而我们可以想到最长路这件事,就可以联想到树的直径。
令从 \(u\) 出发的最长路所到达的点为 \(v\)。
可以证明,\(v\) 肯定是某一条树的直径的某一个端点,因为有一条直径的某个端点本身就是从点 \(u\) 开始的能走到的最远的点,所以 \(v\) 一定是这个搜到的点。我们设这条直径的两个端点为 \(s, t\)。
所以,我们可以画一个这样的图:
也就是说,如果我们要求与 \(u\) 距离为 \(k\) 的点,就相当于在以 \(s\) 为根的树和以 \(t\) 为根的树上找 \(u\) 的 \(k\) 级祖先。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, q, d, id[2], fa[2][20][N];
vector<int> g[N];
void dfs(int t, int f, int dis, int p) {
if (dis > d) {
d = dis, id[p] = t;
}
if (p >= 2) {
fa[p % 2][0][t] = f;
}
for (int i : g[t]) {
if (i != f) {
dfs(i, t, dis + 1, p);
}
}
}
void get_fa() {
for (int i = 0; i < 2; i++) {
for (int j = 1; j < 20; j++) {
for (int k = 1; k <= n; k++) {
fa[i][j][k] = fa[i][j - 1][fa[i][j - 1][k]];
}
}
}
}
int Find(int u, int k, bool p) {
for (int i = 0; i < 20; i++, k >>= 1) {
if (k & 1) {
u = fa[p][i][u];
}
}
return u;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1, a, b; i < n; i++) {
cin >> a >> b;
g[a].push_back(b), g[b].push_back(a);
}
dfs(1, 0, 0, 0), d = 0, dfs(id[0], 0, 0, 1);
dfs(id[0], 0, 0, 2), dfs(id[1], 0, 0, 3), get_fa();
cin >> q;
while (q--) {
int u, k;
cin >> u >> k;
int s = max(Find(u, k, 0), Find(u, k, 1));
cout << (!s ? -1 : s) << '\n';
}
return 0;
}
离线
我们可以把每个点 \(u\) 需要查询的每个 \(k\) 都记录下来,然后对树做一遍深搜,用栈来维护每一条树链的元素,记录每次询问的答案。
代码
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int N = 2e5 + 10;
int n, q, d, id[2], ans[N];
vector<int> g[N];
vector<pii> pos[N];
int top, stk[N];
void dfs(int t, int f, int dis, int p) {
if (dis > d) {
d = dis, id[p] = t;
}
for (int i : g[t]) {
if (i != f) {
dfs(i, t, dis + 1, p);
}
}
}
void _dfs(int t, int fa) {
stk[++top] = t;
for (int i : g[t]) {
if (i != fa) {
_dfs(i, t);
}
}
for (auto [i, j] : pos[t]) {
if (top > i) {
ans[j] = stk[top - i];
}
}
top--;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1, a, b; i < n; i++) {
cin >> a >> b;
g[a].push_back(b), g[b].push_back(a);
}
dfs(1, 0, 0, 0), d = 0, dfs(id[0], 0, 0, 1);
cin >> q;
for (int i = 1, u, k; i <= q; i++) {
cin >> u >> k, ans[i] = -1;
pos[u].push_back({k, i});
}
_dfs(id[0], 0), _dfs(id[1], 1);
for (int i = 1; i <= q; i++) {
cout << ans[i] << '\n';
}
return 0;
}