分析
-
使用倍增法解决即可。
-
因为树中最多有四万个点,树的高度最多为四万,$\lfloor log(40000) \rfloor = 15, (2^{15}=32768)$,因此fa的第二维有0-15一共16个数,第二维需要设置为16。
-
这一题的处理方式是在线方式,即读取一个询问,处理一个询问。与此对应的是离线方式,即将所有询问读取完毕,一起进行处理,可以使用tarjan算法,对应题目是:AcWing 1171. 距离。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 40010, M = N * 2;
int n, m; // 节点数,询问数
int h[N], e[M], ne[M], idx;
int fa[N][16];
int depth[N]; // 节点深度,根节点深度规定为1, depth[0]规定为0
int q[N]; // bfs使用的队列
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 负责预处理,求解fa和depth
void bfs(int root) {
memset(depth, 0x3f, sizeof depth);
depth[0] = 0; // 设置哨兵
depth[root] = 1;
int hh = 0, tt = -1;
q[++tt] = root;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q[++tt] = j;
fa[j][0] = t;
for (int k = 1; k <= 15; k++)
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a, int b) {
if (depth[a] < depth[b]) swap(a, b);
// 步骤1: a, b跳到同一层
for (int k = 15; k >= 0; k--)
if (depth[fa[a][k]] >= depth[b]) // 哨兵起作用位置1
a = fa[a][k];
if (a == b) return a;
// 步骤2: a, b跳到他们的最近公共祖先的下一层
for (int k = 15; k >= 0; k--)
if (fa[a][k] != fa[b][k]) { // 哨兵起作用位置2
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
int main() {
scanf("%d", &n);
memset(h, -1, sizeof h);
int root = 0;
for (int i = 0; i < n; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (b == -1) root = a;
else add(a, b), add(b, a);
}
bfs(root);
scanf("%d", &m);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
int p = lca(a, b);
if (p == a) puts("1");
else if (p == b) puts("2");
else puts("0");
}
return 0;
}