最近公共祖先LCA(学习笔记)
\(LCA\)
P3379 【模板】最近公共祖先(LCA)
定义:
在一棵树上,任意两个节点,往上找的最近的公共根节点就是他们两个的最近公共祖先\(LCA\)
作用:
可用于求树上任意两个点的路径和其路径的权值关系,如边权最大值,边权和等,都是以\(logn\)的复杂度
实现:
倍增:
倍增,顾名思义,运用了指数打表的思想。
1.我们要用\(dfs\)遍历每一点,预处理求出每一节点的\(i\)次倍后的父亲节点,及\(f_{x,i}\)表示点\(x\)的\(2^i\)个祖先,并且找到每一个节点的深度
void dfs(int u,int fa){
d[u]=d[fa]+1;
f[u][0]=fa;
for(int i=1;(1<<i)<=d[u];i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa)continue;
dfs(v,u);
}
}
2.倍增求\(LCA\)时,我们先要确保要找的点在同一深度。让后一起向上跳求\(LCA\)。因此先要判断两节点的深度,在将深的往上跳,直到跳到同一深度。这时要判断是否相等,预防一个节点就是另一节点的根节点。相等则直接返回
3.等两节点在同一高度后,在同时向上跳,知道跳完,这是,两节点所在节点就是\(LCA\),输出即可,若要求路径关系,在\(dfs\)时顺便也预处理一下,数值,后面相同
int lca(int x,int y){
if(d[x]<d[y])swap(x,y);
for(int i=20;i>=0;i--){
if(d[f[x][i]]<d[y])continue;
x=f[x][i];
}
if(x==y)return x;
for(int i=20;i>=0;i--){
if(f[x][i]==f[y][i])continue;
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
注意:
1.\(dfs\)时\(f_{i,0}\)就是其父节点
2.要给根节点的父节点和根节点深度赋值
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+10;
vector<int>e[N];
int f[N][32],d[N];
void dfs(int u,int fa){
d[u]=d[fa]+1;
f[u][0]=fa;
for(int i=1;(1<<i)<=d[u];i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa)continue;
dfs(v,u);
}
}
int lca(int x,int y){
if(d[x]<d[y])swap(x,y);
for(int i=20;i>=0;i--){
if(d[f[x][i]]<d[y])continue;
x=f[x][i];
}
if(x==y)return x;
for(int i=20;i>=0;i--){
if(f[x][i]==f[y][i])continue;
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int main(){
int n,m,s;
cin>>n>>m>>s;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
d[s]=1;
dfs(s,0);
while(m--){
int u,v;
cin>>u>>v;
cout<<lca(u,v)<<endl;
}
}
练习:
\(1.Cheap Robot\) 题解
\(2.P4281 [AHOI2008]\) 紧急集合 / 聚会 题解
\(3.P5836 [USACO19DEC] Milk Visits S\) 题解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682361
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步