LCA即最近公共祖先。
百度百科给的定义是,对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u和v的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先。
上面说的深度是将根结点的深度视为最小。
加深一下理解,如果u和v的最近公共祖先是x,那从u到v一定要经过x。
同时也可以这样理解,以x为根的子树,是包含u和v的最小树,即从原树中取任意一节点为根的子树,如果其同时包含u和v,那么它一定是x或它的祖先。
对于两个节点的lca,根据两个节点的关系分为一下两种:
一、其中一方是另一方的祖先
那u和v的公共祖先就是是祖先的那一方
二、双方都不是对方的祖先
u和v的祖先x的深度比u和v小,我们先找出u和v中深度较大的一个,然后让其向上跳,直到两个节点的深度相同,当深度相同时,两个节点开始同时往上跳,直到两个节点重合即停止,可以发现这种方法也适用于第一种情况。
那倍增法是如何做的呢?
它的核心思想其实就是对于每个节点求出它跳2^k次的对应的节点。
那我在求解的时候,如果需要跳n次,我们把n转为二进制,跳logn次就可以求出对应的节点。
所以它需要维护两个数组
数组dep dep[i]表示i节点的深度 可以dfs求出
数组 g g[i][j]表示节点i跳2^j次对应的节点 它的递归表达式为 g[i][j]=g[g[i][j-1]][j-1]
详见代码和注释:
const int maxn=1e5+50;
int dep[maxn],g[maxn][21];
inline int lca(int x,int y){
if(dep[x]<dep[y])swap(x, y);
for(int i=20;i>=0;i--)//将x和y跳到同一个高度
if((1<<i)<=dep[x]-dep[y])
x=g[x][i];
if(x==y) return x; //第一种情况 y是x的祖先
for(int i=20;i>=0;i--)//一定要从大到小,如果跳的次数大于最近公共祖先需要的次数 g[x][i]==g[y][i]
if(g[x][i]!=g[y][i])
x=g[x][i],y=g[y][i];//两个节点同时往上跳
return g[x][0];
}
inline void dfs(int x,int fa){
dep[x]=dep[fa]+1;
g[x][0]=fa;
for(int i=1;i<=20;i++)
g[x][i]=g[g[x][i-1]][i-1];
for(int i=head[x];i;i=nxt[i])
if(to[i]!=fa)
dfs(to[i],x);
}
相关题目:
1.洛谷P1967 货车运输
LCA+生成树
题解