#include<iostream> #include<cstdio> usingnamespacestd; constint maxn = 500500; constint maxlog = 20; int n, m, s, cnt; int head[maxn], fa[maxn][25], depth[maxn]; structNode { int to, next; } G[maxn * 2]; voidaddedge(int u, int v)//链式前向星存图 { ++cnt; G[cnt].to = v; G[cnt].next = head[u]; head[u] = cnt; } voiddfs(int u)//DFS计算出深度,初始化fa[u][0] { depth[u] = depth[fa[u][0]] + 1; for (int i = head[u]; i; i = G[i].next) { int v = G[i].to; if (v == fa[u][0]) continue; fa[v][0] = u; dfs(v); } } intlca(int x, int y)//查询LCA { if (depth[x] < depth[y]) //钦定x为深度较大的节点,若不然,则交换x, y的值 swap(x, y); for (int i = maxlog; i >= 0; i--) { if (depth[fa[x][i]] >= depth[y]) //如果x往上跳时,深度比y高,说明可以跳 x = fa[x][i]; } if (x == y) //如果同步深度之后正好在同一节点,说明这就是LCA return x; for (int i = maxlog; i >= 0; i--) { if (fa[x][i] == fa[y][i]) //如果是第2^i个父节点是同一个,说明跳过头了 continue; x = fa[x][i]; //如果不是同一个,说明可以跳 y = fa[y][i]; } return fa[x][0]; //最后x的第1个父节点就是LCA } intmain() { scanf("%d %d %d", &n, &m, &s); for (int i = 1; i < n; i++) { int x, y; scanf("%d %d", &x, &y); addedge(x, y); addedge(y, x); } dfs(s); for (int j = 1; j <= maxlog; j++) //应先固定j,找出所有i的第2^j个父节点,否则会出错 { for (int i = 1; i <= n; i++) { fa[i][j] = fa[fa[i][j - 1]][j - 1]; } } for (int i = 1; i <= m; i++) { int a, b; scanf("%d %d", &a, &b); printf("%d\n", lca(a, b)); } return0; }
Tarjan 算法
思路:
利用 Tarjan 算法求 LCA 需要结合并查集。
初始化并查集,对于每个节点 u ,设 fa[u]=u 。
在 Tarjan 算法的执行过程中,如果当前 DFS 到点 u ,先继续 DFS 其子节点 v ,再将 fa[v] 设为 u 。
对于以 u 为根的子树,考察其下的一对 LCA 查询 a,b 。若 a 已经被 DFS 完成,则记录其完成状态,当 DFS 至 b 时,发现 a 已被访问,此时使用并查集查询 a 所属并查集的根节点,由于此时以 u 为根的子树还没有 DFS 完成,即 fa[u]=u ,故查询结果为 u 本身, u 即为 a,b 的最近公共祖先 LCA。