LCA-最近公共祖先总结

简介

LCA\operatorname{LCA} ,即最近公共祖先,是指在有根树中,找出某两个结点 uuvv 最近的公共祖先。

作用

求树上两点最短路径。

朴素算法

不停找爸爸祖先,共同访问的第一个即最近公共祖先。

倍增

why?

具有单调性,最近公共祖先的祖先同样是公共祖先。

tree114131211104321tree2987654321LCA?NNNNNYYYY\begin{array}{|c|c|c|c|c|c|c|c|c|c|} \hlinetree_1 &14&13&12&11&10&4&3&2&1\\ \hlinetree_2 &9&8&7&6&5&4&3&2&1\\ \hlineLCA? &N&N&N&N&N&Y&Y&Y&Y \\ \hline\end{array}

思路:

  1. u,vu,v 变成同一深度的。(同步调整深度)

  2. 由大到小倍增同步跳转,找到公共祖先的下一个点(再上一步就是答案,防止跳过头)。

实现思路:

通过预处理 fx,i\text{f}_{x,i} 数组,游标可以快速移动,大幅减少了游标跳转次数。fx,i\text{f}_{x,i} 表示点 xx 的第 2i2^i 个祖先。fx,i\text{f}_{x,i} 数组可以通过 dfs 预处理出来。

fax,i\text{fa}_{x,i} 求法

使用动态规划思想,状态转移方程为:

fx,i=ffx,i1,i1f_{x,i}=f_{f_{x,i-1},i-1}

(第 2i2^i 个祖先为第 2i12^{i-1} 个祖先的第 2i12^{i-1} 个祖先,挺像区间 dp)。

代码~~

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int n,m,root,x,y,nm;
int head[N],
	f[N][20],//第 i 个点的第 2^j 个祖先 
	dep[N];//第 i 个点 的深度,定义 根节点 深度为 1  
struct sb{
	int to,next; 
}e[N*2];
void ae(int from,int to) {
	e[++nm].to=to;
	e[nm].next=head[from];
	head[from]=nm;
}
void dfs(int from,int fa) {
	dep[from]=dep[fa]+1; 
	for(int i=head[from];i;i=e[i].next) {
		int to=e[i].to;
		if(to==fa) continue;
		f[to][0]=from;
		for(int k=1;1<<k<=dep[from];k++) {
			f[to][k]=f[f[to][k-1]][k-1];//预处理 fa 
		}
		dfs(to,from);
	}
}
int lca(int x,int y) {
	if(dep[x]<dep[y]) {
		swap(x,y);// 默认为将 x 调整至 y 
	}
	for(int i=19;i>=0;i--) {
		if(dep[f[x][i]]>=dep[y])//跳至同一高度 
			x=f[x][i];
	}
	if(x==y) return y;//在同一个高度,是一个节点 -> y为 LCA 
	for(int i=19;i>=0;i--) 
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];//倍增同步跳转 跳到的是 LCA 的儿子 
	return f[x][0];//父节点就是 LCA
}
int main() {
	cin>>n>>m>>root;
	for(int i=1;i<n;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		ae(x,y),ae(y,x);
	}
	dfs(root,0);//从根开始 
	for(int i=1;i<=m;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y)); 
	}
	return 0; 
}

tarjan

代码~~

以下为 tarjan 带权求离线最短路代码

#include<bits/stdc++.h>
using namespace std;
#define pll pair<int,int>
const int N = 5e5+10;
struct sb{
	int dis,next,to;
}edge[N*2];
int fa[N],dis[N],res[N],head[N],n,m,nm;
bool vis[N];
vector<pll >vec[N];
void ae(int from,int to,int dis) {
	edge[++nm].dis=dis;
	edge[nm].next=head[from];
	edge[nm].to=to;
	head[from]=nm;
}
int find(int x) {if(x!=fa[x]) return fa[x]=find(fa[x]);return fa[x];}
void dfs(int from) {
	vis[from]=1;
	for(int i=head[from];i;i=edge[i].next) {
		int to=edge[i].to;
		if(!vis[to]) {
			dis[to]=dis[from]+edge[i].dis;
			dfs(to);
			fa[to]=from;
		}
	}
	for(int i=0;i<vec[from].size();i++) {
		int to=vec[from][i].first,id=vec[from][i].second;
		if(vis[to]) res[id]=dis[to]+dis[from]-2*dis[find(to)]; 
	}
}
int main() {
	cin>>n>>m;
	for(int i=1;i<n;i++) {
		int x,y,z;
		cin>>x>>y>>z;
		ae(x,y,z),ae(y,x,z);
	}
	for(int i=1;i<=n;i++) {
		fa[i]=i;
	}
	for(int i=1;i<=m;i++) {
		int x,y;
		cin>>x>>y;
		vec[x].push_back({y,i});
		vec[y].push_back({x,i});
	}
	dfs(1);
	for(int i=1;i<=m;i++) {
		cout<<res[i]<<"\n";
	}
}

本文作者:cjrqwq

本文链接:https://www.cnblogs.com/yfzqwq/p/18492826

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   cjrqwq  阅读(33)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
展开
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.