[模板]用倍增求LCA问题

定义

LCA,最近公共祖先,是指一棵树上两个节点的深度最大公共祖先。也可以理解为两个节点之间的路径深度最小的点。
我们这里用了倍增的方法求了LCA。
我们的基本的思路就是,用dfs遍历求出所有点的深度。father[i][j]数组用来求的是距离节点i,距离 2j 的祖先。可以知道,father[i][0]就是它的直接父亲。然后通过倍增的思路求出father数组的所有元素。然后进行lca。求lca的基本思路是:先让深度较大的点向上跳,这里写图片描述
然后x和y再同时向上跳2的幂,总会跳到这样两个点,他们的父亲结点是同一个点,那就是x和y的LCA。

code

首先我们需要用邻接表建一颗参天大树~

namespace graph
{
    int n,m,s,top=0;
    int head[maxn];
    struct Hy
    {
        int dot_order,next_location;
    }a[maxn*2];
    //邻接表
    void insert_edge(int x,int y)
    {
        top++;
        a[top].dot_order=y;
        a[top].next_location=head[x];
        head[x]=top;
    }
    //插入一条x到y的边
    void init()
    {
        n=read();
        m=read();
        s=read();
        //这里n是点数,m是边数,s是根节点编号
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            x=read();
            y=read();
            insert_edge(x,y);
            insert_edge(y,x);
        }
    }
}using namespace graph;
//建图建树基本操作

然后开始重头戏——倍增。

int depth[maxn],father[maxn][64];
/*depth数组用来记录当前点的深度
father[i][j]代表距离i 2^j的祖先
*/

dfs函数求深度和直接父亲:

    void hy(int u)
    {
        for(int i=head[u];i;i=a[i].next_location)
        //遍历邻接表
        {
            int to=a[i].dot_order;
            if(to==father[u][0])continue;
            //如果to是u的父亲,那么就说明这条边被访问过了,不能再回溯了
            depth[to]=depth[u]+1;
            //更新深度
            father[to][0]=u;
            //更新父亲结点
            hy(to);
            //继续深度优先遍历
        }
    }

这个时候我们已经求出了所有的 father[i][0] ,我们需要计算 i <script id="MathJax-Element-31" type="math/tex">i</script>的其他祖先。
预处理father数组:

    void father_chuli()
    {
        for(int j=1;(1<<j)<=n;j++)
        {
            for(int i=1;i<=n;i++)
                father[i][j]=father[father[i][j-1]][j-1];
                /*有点类似RMQ问题啊。。。
                i的2^(j-1)级祖先的2^(j-1)级祖先就是
                I的2^I级祖先。
                */
        }`

    }

好了,现在所有的预处理工作都完成了。我们开始随便搞~

int lca(int a,int b)
    {
        if(depth[a]>depth[b])swap(a,b);
        //让b的深度更深
        int f=depth[b]-depth[a];
        //f是b和a的深度之差
        for(int i=0;(1<<i)<=f;i++)
        {
            if((1<<i)&f)b=father[b][i];
        }//让b跳到跟a相同高度上
        if(a!=b)/*如果a和b不是同一个结点那么就要继续跳
        如果是同一个结点,那么它就是LCA
        */
        {
            for(int i=(int)log2(n);i>=0;i--)
            //从大到小跳。正确性显然。
            {
                if(father[a][i]!=father[b][i])
                //如果不相等,就说明该节点的深度还是比LCA大
                {
                    a=father[a][i];
                    b=father[b][i];
                    //那就继续跳
                }
            }
            a=father[a][0];
            //这个时候a和b还不是同一个节点,但是a和b的父亲就是x和y的lca。
        }

        return a;
    }

GG

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值