最近公共祖先(LCA)(洛谷)

该代码实现了一个基于广度优先搜索(BFS)的算法来寻找树中两个节点的最近公共祖先。算法首先初始化一个队列用于BFS,并通过层级遍历记录每个节点到根节点的距离。同时,使用动态规划优化查找最近公共祖先的过程。最后,输入两个节点,程序返回它们的最近公共祖先。

P3379
代码:


#include<bits/stdc++.h>
using namespace std;
const int SIZE=500005;
int head[SIZE],nxt[SIZE*2],ver[SIZE*2],tot=0;
int n,m,s,t,d[SIZE],f[SIZE][20];
queue<int> q;
void add(int x,int y){
	ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
void bfs(){
	q.push(s);
	d[s]=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=nxt[i]){
			int y=ver[i];
			if(d[y]) continue;
			d[y]=d[x]+1;
			q.push(y);
			f[y][0]=x;
			for(int j=1;j<=t;j++)
				f[y][j]=f[f[y][j-1]][j-1];
		}
	}
}
int lca(int x,int y){
	if(d[x]>d[y]) swap(x,y);
	for(int i=t;i>=0;i--)
		if(d[f[y][i]]>=d[x]) y=f[y][i];
	if(x==y) return x;
	for(int i=t;i>=0;i--)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	t=(int)(log(n)/log(2))+1;
	for(int i=1;i<=n-1;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	bfs();
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eq1Eo5tB-1633441199726)(https://api.nmb.show/1985acg.php)]

### 算法原理 倍增法求解最近公共祖先LCA)的核心在于利用二进制的特性进行高效的祖先查找。首先,给定一棵树,通过深度优先搜索(DFS)从根节点开始遍历,计算每个节点的深度,并初始化关键数组`fa[i][j]`,该数组表示从`i`节点往根节点出发的第$2^j$个祖先节点,其中`fa[i][0]`即`i`节点的父节点。 在查询两个节点`u`和`v`的LCA时,先让深度较深的节点`u`通过`fa`数组进行跳跃,使得`u`和`v`处于相同的深度。这是因为任意一个大于等于0的数都可以由$1, 2, 4, 8, 16, 32, \cdots$这些数凑出来。之后,如果`u`和`v`相等,那么这个节点就是最近公共祖先;若不相等,则让`u`和`v`同时通过`fa`数组向上跳跃,直到它们的某一级祖先不同,最终返回`fa[u][0]`,即`u`的父节点,此节点即为最近公共祖先 [^1][^4]。 另外,两点集并的最近公共祖先为两点集分别的最近公共祖先最近公共祖先,即$LCA(A \cup B) = LCA(LCA(A), LCA(B))$,利用这个性质,可以求解任意多节点之间的最近公共祖先 [^2][^3]。 ### 代码实现 以下是使用倍增法求解最近公共祖先LCA)的核心代码: ```python # lcaMultiply 函数返回u和v的父节点 def lcaMultiply(u, v): # 倍增 if depth[u] < depth[v]: u, v = v, u # 让u指向深度最大的节点 for i in range(19, -1, -1): # 向上找u的祖先,通过数学可以推出来,最终u和v的深度一定会相等,因为两者深度差为一个大于等于0的数,如果等于0什么都不做,对于大于0的数,一定可以通过1,2,4,8,16,32,...,凑出来 if depth[fa[u][i]] >= depth[v]: u = fa[u][i] if u == v: return u # 如果此时u和v刚好相等,那一定是最近公共祖先,直接返回即可 for i in range(19, -1, -1): # 两个一起往上找祖先 if fa[u][i] != fa[v][i]: # 如果两个祖先不一样,就继续往上走 u = fa[u][i] # 向上走到祖先 v = fa[v][i] return fa[u][0] ``` ### 应用示例 在 P3379 【模板】最近公共祖先LCA)这道题中可以使用倍增法求解。该算法还可用于计算树上两点间的距离,公式为$d(u, v) = h(u) + h(v) - 2h(LCA(u, v))$,其中$d$是树上两点间的距离,$h$代表某点到树根的距离 [^3]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值