LCA的倍增算法

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+生成树
题解

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值