【数据结构】浅谈倍增求LCA

时间:2023-03-09 02:25:57
【数据结构】浅谈倍增求LCA

思路

运用树上倍增法可以高效率地求出两点x,y的公共祖先LCA

我们设f[x][k]表示x的2k辈祖先

f[x][0]为x的父节点

因为从x向根节点走2k 可以看成从x走2k-1步 再走2k-1

所以对于1≤k≤logn 有f[x][k]=f[f[x][k-1]][k-1] (类似二分思想)

预处理:

因此我们可以对树进行遍历后得到所有f[x][0] 再计算出f数组的所有值

求LCA:

设dep[x]为x的深度 设dep[x]≥dep[y](否则 可以交换x和y)

使用二进制拆分 把x和y调整到同一深度

若此时x与y相等 则已经找到LCA

若不相等 则x和y同时向上跳同一深度 直到他们的父节点相同 则他们的父节点就是LCA

代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define maxn 500050
#define INF 1e9+7
int n,m,cnt,ans,a,b,k,x,y;
int h[maxn],dep[maxn],f[maxn][];
struct Edge
{
int next;
int to;
}e[maxn<<];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=h[u];
h[u]=cnt;
}
void deal(int u,int fa)
{
dep[u]=dep[fa]+;//子节点深度等于父节点+1
for(int i=;i<=;i++)
{
f[u][i]=f[f[u][i-]][i-];//计算u的2^k辈祖先
}
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;//如果是父节点就退出
f[v][]=u;//v是u的子节点
deal(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);//如果x的深度比y小 就交换
for(int i=;i>=;i--)//从大到小循环 先走大步 如果不行在走小步
{
if(dep[f[x][i]]>=dep[y]) x=f[x][i];//当走完深度还是大于y 就走
if(x==y) return x;//当x=y时 找到LCA
}
for(int i=;i>=;i--)//此时x和y已经在同一层了
{
if(f[x][i]!=f[y][i])//如果往上走之后还没有到LCA 就往上走
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][];//返回LCA
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);//无向图
}
deal(,);//预处理
for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
}