LCA(最近公共祖先)学习笔记

0.LCA是什么

LCA指最近公共祖先。他有许多的求法和应用。

1.LCA求法

首先最简单的:跳到同一深度后,再一起向上跳,跳到同一节点为止。
这个方法时间复杂度最坏为\(O(n)\),如果不是一个老善人是不会让你过的。
如何优化这个方法呢?我们可以试试倍增,每次上跳多个节点,这样时间复杂度就只有\(\log n\)了。
上P3379代码,可以结合代码食用:

#include<bits/stdc++.h>
using namespace std;
int n,q,head[500001],father[500001][21],tot,h[500001]= {-1},s;
inline void read(int &x){
	int dat=0,oko=1;char chc=getchar();
	while(chc<'0'||chc>'9'){if(chc=='-')oko=-1;chc=getchar();}
	while(chc>='0'&&chc<='9'){
	dat=dat*10+chc-'0';chc=getchar();
	}
	x=dat*oko;
}
struct tu {
	int to,pre;
} edge[1000001];
void insert(int x,int y) {
	tot++;
	edge[tot].to=y;
	edge[tot].pre=head[x];
	head[x]=tot;
}
void dfs(int x,int fa) {
	h[x]=h[fa]+1;
	father[x][0]=fa;
	for(int i=head[x]; i; i=edge[i].pre) {
		int node=edge[i].to;
		if(node!=fa) {
			dfs(node,x);
		}
	}
}
int LCA(int x,int y) {
	if(h[x]!=h[y]) {
		if(h[x]<h[y])swap(x,y);
		for(int i=20;i>=0;i--){
			if(h[father[x][i]]>=h[y])x=father[x][i];
		}
	}
	if(x==y)return x;
	int	p=0;
	for(int i=20;i>=0;i--){
		if(father[x][i]!=father[y][i]){
			x=father[x][i];
			y=father[y][i];
		}
	}
	return father[y][0];
}
int main() {
	read(n),read(q),read(s);
	for(int i=1; i<n; i++) {
		int x,y;
		read(x),read(y);
		insert(x,y);
		insert(y,x);
	}
	dfs(s,0);
	for(int i=1; i<=20; i++) {
		for(int j=1; j<=n; j++) {
			father[j][i]=father[father[j][i-1]][i-1];//预处理倍增数组
		}
	}
	while(q--) {
		int x,y;
		read(x),read(y);
		printf("%d\n",LCA(x,y));
	}
	return 0;
}

2.LCA应用

1.任意两点求距离
预处理所有点到根为\(d_i\),询问\(dis_{x,y}\)\(d_x+d_y-2\times d_{lca}\)
2.树上区间加,附查询
如果动态就要树剖或LCT,但静态可用树上差分。
具体在\(x,y\)都加,\(lca\)位置将其抵消。

posted @ 2022-05-21 15:59  Andy__Lin  阅读(138)  评论(0)    收藏  举报