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\)位置将其抵消。

浙公网安备 33010602011771号