最近公共祖先


最近公共祖先简称LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点所有的公共祖先里面,离根最远的那个。

  • \(d(u,v)=h(u)+h(v)-2*h(lca(u,v))\)

例题
P3379 【模板】最近公共祖先(LCA)

Tarjan求解最近公共祖先

基本思路
1.dfs整棵树
2.每到达一个结点,先标记这个节点已被访问
3.随后遍历与该结点又访问关系的结点(记为z),若z已经被访问过,那么二者的lca就是getf(z)。
4.接着向孩子结点遍历,回溯的时候将孩子结点与当前结点用并查集合并。
参考代码

#include
using namespace std;
const int N=5e5+10;
vectornode[N];
int f[N];
int getf(int a){
	return f[a]==a?a:f[a]=getf(f[a]);
}
int n,m,s;
int book[N];
struct{
	int to,next;
}e[2*N];
int head[N],cnt;
inline void add(int a,int b){
	e[++cnt].to=b;
	e[cnt].next=head[a];
	head[a]=cnt;
}
int ans[N];
void Tarjan(int s){
	book[s]=1;
	int to;
	for(int i=head[s];i;i=e[i].next){
		to=e[i].to;
		if(book[to])ans[(i+1)/2]=getf(to);
	}
	for(auto i:node[s]){
		if(book[i])continue;
		Tarjan(i);
		f[i]=s;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	int a,b;
	for(int i=1;i<=n;++i)f[i]=i;
	for(int i=1;i

倍增

倍增是对朴素算法的优化。对于要求解的两个点a,b(假设a的深度大于b),先将两个结点上升到同一高度,然后再一起向上跳,这个过程用二进制就很快了。
参考代码

#include
using namespace std;
const int N=5e5+10;
const int M=5e5+10;
struct{
	int to,next;
}e[2*M];
int head[N],cnt,dep[N];
int n,m,s;
inline void add(int a,int b){
	e[++cnt].to=b;
	e[cnt].next=head[a];
	head[a]=cnt;
}
int len;
int fa[N][30];//fa[i][j]表示i结点的2^j个祖先
void dfs(int x,int f){
	fa[x][0]=f;
	dep[x]=dep[f]+1;
	int to;
	for(int i=1;(1<=0;--i){//这里可以直接理解为二分查找
		if(fa[a][i]!=fa[b][i]){
			a=fa[a][i];
			b=fa[b][i];
		}
	}
	return fa[a][0];
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	int a,b;
	while((1<

基于DFS序的倍增

众所周知,可以很快的判断出两个节点的祖孙关系。
我们只需将深度大的点一直向上跳(还是基于二进制或二分的思想跳),并不断判断祖孙关系即可。
这种方法不用将两个点升到同一高度,并且常数更小。
参考代码

#include
using namespace std;
const int N=5e5+10;
const int M=5e5+10;
struct{
	int to,next;
}e[2*M];
int head[N],cnt,dep[N];
int n,m,s;
inline void add(int a,int b){
	e[++cnt].to=b;
	e[cnt].next=head[a];
	head[a]=cnt;
}
int len;
int fa[N][30];
int dfn[N],se[N],ti;
void dfs(int x,int f){
	fa[x][0]=f;
	dep[x]=dep[f]+1;
	int to;
	dfn[x]=++ti;
	se[x]=1;
	for(int i=1;(1<=dfn[a])return b;
	for(int i=len;i>=0;--i){
		int na=fa[a][i];
		if(!na)continue;//这个地方小心跳穿了
		if(!(dfn[na]<=dfn[b]&&dfn[na]+se[na]-1>=dfn[b]))
		a=na;
	}
	return fa[a][0];
	if(a==b)return a;
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	int a,b;
	while((1<