LCA(最近公共祖先)之倍增算法
概述
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
如图,3和5的最近公共祖先是1,5和2的最近公共祖先是4
在本篇中我们先介绍一下倍增算法
我们需要一个数组de[i]来表示每一个节点i的深度,用另一数组parent[i][j]来表示每一节点j向上走2的i次方是哪个节点
我们首先在初始化中算出每个点的深度和它的上一个点是什么(用parent[0][i]表示)
在此后我们进行倍增的处理:parent[1][j]=parent[0][parent[0][j]]......parent[i+1][j]=parent[i][parent[i][j]]
当然如果已经走到根节点了,就将其它的parent全设为0
然后我们就可以搞lca了:给你两个点想x,y,让y成为深的那个,如果x,y深度不等就让y倍增地往上跳。
当x,y深度相等时凡是它俩不相等就倍增地跳,最后它们中任意一个的父节点及他们的最近公共祖先
模板
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int LOG=20;
int head[1000100],nxt[1000100],to[1000100],cnt;
int de[500010];
int parent[LOG+3][500010];
inline void read(int &x){
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
inline void add(int x,int y){
nxt[++cnt]=head[x];to[cnt]=y;head[x]=cnt;
nxt[++cnt]=head[y];to[cnt]=x;head[y]=cnt;
}
inline void dfs(int now,int dep,int be)
{ parent[0][now]=be;de[now]=dep;
for(int i=head[now];i;i=nxt[i])
if(to[i]!=be)dfs(to[i],dep+1,now);
}
inline int lca(int x,int y)
{ if(de[x]>de[y])swap(x,y);
for(int i=LOG;i>=0;i--)
if(de[y]-de[x]>=(1<<i))y=parent[i][y];
if(x==y)return x;
for(int i=LOG;i>=0;i--)
if(parent[i][x]!=parent[i][y])x=parent[i][x],y=parent[i][y];
return parent[0][x];
}
int main()
{ int n,m,s,i,j,k,p,q;
read(n),read(m),read(s);
for(i=1;i<n;i++){
int x,y;
read(x),read(y);
add(x,y);
}
dfs(s,0,0);
for(i=0;i<=LOG;i++)
for(j=1;j<=n;j++)
if(parent[i][j]<=0)parent[i+1][j]=-1;
else parent[i+1][j]=parent[i][parent[i][j]];
for(i=1;i<=m;i++){
int x,y;
read(x),read(y);
printf("%d\n",lca(x,y));
}
return 0;
}