\(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\) 题解