最近公共祖先模板
最近公共祖先模板
DFS序求LCA
时间复杂度:\(O(N\log N+M)\)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
int N,M,S,cnt,dfn[MAXN],st[20][MAXN],lg[MAXN];//dfn[x]:节点x的DFS序(从1开始);st[0][i]:DFS序为i的节点的父节点,这是后续ST表的基础;st[k][i]:区间[i,i+2^k-1]内dfn最小的节点(是父节点编号)
int hd[MAXN],tot,nxt[2*MAXN],ver[2*MAXN];
void add(int u,int v){
ver[++tot]=v;nxt[tot]=hd[u];hd[u]=tot;
ver[++tot]=u;nxt[tot]=hd[v];hd[v]=tot;
}
void dfs(int id,int f){//求出dfn,并记录父节点
dfn[id]=++cnt;//给节点id分配DFS序
st[0][dfn[id]]=f;//进入id时,其父节点为f,记录其父节点
for(int i=hd[id];i;i=nxt[i]){
if(ver[i]!=f) dfs(ver[i],id);
}
}
int Get(int x,int y){//比较两个节点的dfn,返回dfn较小的节点
return dfn[x]<dfn[y]?x:y;
}
int LCA(int u,int v){//O(1)查询LCA
if(u==v) return u;
u=dfn[u],v=dfn[v];
if(u>v) swap(u,v);
int d=lg[v-u];//区间长度为v-u,取对数
u++;//左端点变为u+1,查询区间[u+1,v]
return Get(st[d][u],st[d][v-(1<<d)+1]);//用两个长度2^d的区间覆盖[u+1,v]
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>N>>M>>S;
for(int i=2,u,v;i<=N;i++){
cin>>u>>v;
add(u,v);
}
dfs(S,0);//根节点父亲记为0
for(int i=2;i<=N;i++) lg[i]=lg[i>>1]+1;//预处理log2
for(int i=1;i<=lg[N];i++){//建立ST表
for(int j=1;j+(1<<i)-1<=N;j++){//第i层区间长度为2^i,用两个2^(i-1)的区间合并,取dfn较小的节点
st[i][j]=Get(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
}
for(int i=1,u,v;i<=M;i++){
cin>>u>>v;
cout<<LCA(u,v)<<'\n';
}
return 0;
}
树链剖分求LCA
时间复杂度:\(O(M\log N+N)\)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
int hd[MAXN],tot,nxt[2*MAXN],ver[2*MAXN];
int N,M,S,dep[MAXN],sz[MAXN],zson[MAXN],fa[MAXN],top[MAXN];
void add(int u,int v){
ver[++tot]=v;nxt[tot]=hd[u];hd[u]=tot;
ver[++tot]=u;nxt[tot]=hd[v];hd[v]=tot;
}
void dfs1(int u,int f){//第一遍 DFS:求深度,父节点,子树大小,重儿子
sz[u]=1;
fa[u]=f;
dep[u]=dep[f]+1;//深度=父节点深度+1
for(int i=hd[u];i;i=nxt[i]){
int v=ver[i];
if(v!=f){
dfs1(v,u);
sz[u]+=sz[v];
if(sz[zson[u]]<sz[v]||!zson[u]){//维护重儿子:子树最大的那个儿子或还没有设置重儿子
zson[u]=v;
}
}
}
}
void dfs2(int u,int t){//第二遍DFS:确定每条重链的顶端top
top[u]=t;
if(!zson[u]) return ;//叶子节点
dfs2(zson[u],t);//先走重儿子,继承当前链顶
for(int i=hd[u];i;i=nxt[i]){//遍历轻儿子,它们各自成为新链的顶端
int v=ver[i];
if(v!=fa[u]&&v!=zson[u]){//排除父节点和已经处理过的重儿子
dfs2(v,v);//轻儿子作为新链的顶端,递归剖分
}
}
}
int LCA(int u,int v){//树剖求LCA:不断将深度较大的链顶向上跳
while(top[u]!=top[v]){
//选择链顶深度较大的节点向上跳
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
u=fa[top[u]];//跳至链顶的父节点
}
return dep[u]<dep[v]?u:v;//此时两点在同一条重链上,深度较小的即为LCA
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>N>>M>>S;
for(int i=2,u,v;i<=N;i++){
cin>>u>>v;
add(u,v);
}
dfs1(S,0);//以S为根
dfs2(S,S);//根节点所在重链顶端为自己
for(int i=1,u,v;i<=M;i++){
cin>>u>>v;
cout<<LCA(u,v)<<'\n';
}
return 0;
}

浙公网安备 33010602011771号