LCA(倍增法)
概念
对于有根树 T 的两个结点 u、v,最近公共祖先 \(\operatorname{LCA}(T,u,v)\) 表示一个结点 x,满足 x 是 u 和 v 的祖先且 x 的深度尽可能大。在这里,一个节点也可以是它自己的祖先。 ——摘自 百度百科
求法
基于倍增的思想,我们可以处理出每个节点向上 \(2^k\) 级的祖先。将两个节点的深度统一后,从大往小枚举 k,若不同则向上跳,直到不存在这样的 k 为止,此时两节点的父节点就是原来两个节点的 LCA。
举个例子,如图,求 7 和 13 两点的 LCA 时,我们先将深度较大的 13 号点跳到与 7 号点深度相同的 5 号点,然后两个点向上枚举,得到 LCA 2 号点。
代码
#include<iostream>
#include<cstdio>
#define maxn 500005
using namespace std;
int n,m,root,xx,yy; int log[maxn],st[maxn][30],depth[maxn];
struct node{int to,nex;}a[maxn*2]; int head[maxn*2],tot=0;
void add(int from,int to){a[++tot].to=to;a[tot].nex=head[from];head[from]=tot;}
void dfs(int p,int fa){
st[p][0]=fa; depth[p]=depth[fa]+1;
for(int i=1;i<=log[depth[p]];i++) st[p][i]=st[st[p][i-1]][i-1];
for(int i=head[p];i;i=a[i].nex) if(a[i].to!=fa) dfs(a[i].to,p);
}
int lca(int x,int y){
if(depth[x]<depth[y]) swap(x,y);
while(depth[x]>depth[y]) x=st[x][log[depth[x]-depth[y]]]; if(x==y) return y;
for(int k=log[depth[x]];k>=0;k--) if(st[x][k]!=st[y][k]){x=st[x][k];y=st[y][k];}
return st[x][0];
}
int main(){
scanf("%d%d%d",&n,&m,&root); for(int i=1;i<n;i++){scanf("%d%d",&xx,&yy);add(xx,yy);add(yy,xx);}
log[1]=0; for(int i=2;i<=n;i++) log[i]=log[i-1]+((1<<(log[i-1]+1))==i);
dfs(root,0);
while(m--){scanf("%d%d",&xx,&yy);printf("%d\n",lca(xx,yy));}
return 0;
}