LCA最近公共祖先

概念

最近公共祖先指两个节点在一棵树上深度最大的公共的祖先节点,即这两个点在这棵树上距离最近的公共祖先节点。

Tarjan 算法介绍

使用倍增思想和动态规划。
基本思路:

  1. 任选一个点为根节点,从根节点开始。
  2. 遍历该点 u 所有子节点 v ,并标记这些子节点 v 已被访问过。
  3. 若是 v 还有子节点,返回 2 ,否则下一步。
  4. 合并 v 到 u 上。
  5. 寻找与当前点 u 有询问关系的点 v 。
  6. 若是 v 已经被访问过了,则可以确认 u 和 v 的最近公共祖先为 v 被合并到的父亲节点 a 。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int idx, e[2*N], nxt[2*N], h[N];
int n, m, s, f[N][30], deep[N];
void add(int u, int v){
	nxt[++idx] = h[u];
	h[u] = idx;
	e[idx] = v;
}
void dfs(int u, int fa){
	f[u][0] = fa;
	for(int i = 1; i <= 23; i++) f[u][i] = f[f[u][i-1]][i-1];
	for(int i = h[u]; i; i = nxt[i]){
		int v = e[i];
		if(v == fa) continue;
		deep[v] = deep[u] + 1;
		dfs(v, u);
	}
}
int lca(int x, int y){
	if(deep[x] < deep[y]) swap(x, y);
	for(int i = 23; i >= 0; i--){
		if(deep[f[x][i]] >= deep[y]) x = f[x][i];
	}
	if(x == y) return x;
	for(int i = 23; i >= 0; i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
int main(){
  deep[1] = 1;
	scanf("%d%d%d", &n, &m, &s);
	for(int i = 1; i < n; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	dfs(s, s);
	while(m--){
		int x, y;
		scanf("%d%d", &x, &y);
		printf("%d\n", lca(x, y));
	}
	return 0;
}
posted @ 2026-07-20 21:18  宰杀方向盘  阅读(9)  评论(0)    收藏  举报