倍增法求LCA

下图是这篇文章的思路导航,本人将按照这个模式学习该算法。此文为我的学习笔记,如有差错感谢大家斧正。

一,最初的目的

     显然就是求LCA了,LCA是啥? 中文解释为树上两点的最近公共祖先。有一个小bug,当两个点在同一条支上的时候,LCA是可以为其中一个点的。也就是说,LCA是可以包括着两个点本身的。并不是严格意义上的祖先。

   这里就加入一道板子题了:(题目链接P3379 【模板】最近公共祖先(LCA))

    题目答意:给你一棵树,有 m 次查询,每次问你 a, b,树上两点的最短距离。

    万物始于暴力,LCA的朴素算法显然也很简单,就是先让深度较深的向上爬直到两点在同一深度上,如果这时两点相遇(一点在以另一点为根的子树上)则LCA为当前位置。如果还没有相遇,就让两点同时向上移动,直到两点相遇(两点都在以LCA为根的子树上)。这里的移动显然是最耗费时间,如果有n个点m次查询就为O(nlog n)当树退化时接近O(n^{2})

二,算法结构

    首先这是一个树,不知道的可以出门找度娘。

    倍增显然就是需要加速朴素算法中的上升过程了,这里运用了一个类似DP的东西。

    我们要初始化一个 anc\left [ i \right ]\left [ j\right ] 数组,这个数组表示, i 这个节点由下往上第 2^{j} 位祖先是谁。显然就有初状态 anc\left [ i \right ]\left [ 0 \right ] 为 i 的父节点,还有状态转移方程  anc\left [ i\right ]\left [ j \right ] = anc\left [ anc\left [ i \right ]\left [ j-1 \right ] \right ]\left [ j-1 \right ] 。这个转移方程的中文表示法就是:我的第 2^{j-1} 位祖先的第 2^{j-1} 位祖先是我的第 2^{j} 位祖先。

    然后的过程就和朴素算法一样了,爬高高。

    第一情况种:

              

     a 上升到与 b 同高相遇,LCA此时为 b 。

     第二种情况:

              

    a b 同高后依然不相遇 , 就让 a b 同时向上  直到 a b 同父。

    然后是代码部分:

struct node_e{
    int v,en;
}e[maxn<<1];
int he[maxn],cnt;//前项星三件套

int vis[maxn],dep[maxn];//标记,深度
int anc[maxn][25];//祖先
int n,m,s;

void ade(int a,int b){
    e[++cnt].v=b;e[cnt].en=he[a];he[a]=cnt;
}
void dfs(int s){//初始化
    vis[s] = 1;
    for(int i=1;i<21;++i){
        if(dep[s]<(1<<i)) break;
        anc[s][i]=anc[anc[s][i-1]][i-1];//倍增求祖先
    }
    for(int i=he[s];i;i=e[i].en){
        int v = e[i].v;
        if(vis[v]) continue;
        dep[v]=dep[s]+1;
        anc[v][0]=s;
        dfs(v);
    }
}
int Lca(int a,int b){
    if(dep[a]<dep[b]){int tt=a;a=b;b=tt;}//确保a在下面
    int t = dep[a]-dep[b];
    for(int i=0;i<21;i++){
        if(t&(1<<i)) a=anc[a][i];//a向上爬到和b同样的高度

    }
    if(a==b) return a;//a,b在一棵子树上
    for(int i=20;i>=0;i--){//不在
        if(anc[a][i]!=anc[b][i]){
            a=anc[a][i];
            b=anc[b][i];
        }
    }
    return anc[a][0];
}

三,理解深入(未完待续)

四,刷题

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值