zoukankan      html  css  js  c++  java
  • 倍增求LCA

    一、引言

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

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

      重头戏——倍增。

    二、预处理

    void Pretreatment(int u,int father)
    {
        deep[u]=deep[father]+1,fa[u][0]=father;//继承父亲的信息 
        for(int i=1,p=lg[deep[u]]-1;i<=p;i++) fa[u][i]=fa[fa[u][i-1]][i-1];//预先处理节点2^k次方祖先 
        for(int i=head[u];i;i=edge[i].next)
            if(edge[i].to!=father)
                Pretreatment(edge[i].to,u);//处理向下的其它树枝 
    }

    三、倍增求LCA

    int Lca(int from,int to)
    {
        if(deep[from]<deep[to]) Swap(from,to);//将from的深度设置为最深 
        while(deep[from]>deep[to]) from=fa[from][lg[deep[from]-deep[to]]-1];//倍增将x跳跃到与y同层 
        if(from==to) return from;//判断是否在同一个节点了 
        for(int i=lg[deep[from]]-1;i>=0;i--) if(fa[from][i]!=fa[to][i]) from=fa[from][i],to=fa[to][i];//倍增跳跃到LCA的下一层 
        return fa[from][0];//返回两者的LCA 
    }

    四、典例分析

    #include<stdio.h>
    #include<stdlib.h>
    #include<math.h>
    #define FORa(i,s,e) for(int i=s;i<=e;i++)
    #define FORs(i,s,e) for(int i=s;i>=e;i--)
    using namespace std;
    
    const int N=500000,M=500000,P=20;
    int n,m,star,cnt,num_edge,head[N+1],fa[N+1][P+2],deep[N+1],lg[N+1];
    //f[u][j]表示节点u向上2^j的祖先,deep[u]表示的是节点u的深度,lg[i]表示log2(i)+1; 
    struct Edge{
        int next,to;
    }edge[2*M+2];
    void Add_edge(int from,int to){
        edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge;
    }
    inline void Swap(int &fa,int &fb){int t=fa;fa=fb,fb=t;}
    
    void Pretreatment(int u,int father)
    {
        deep[u]=deep[father]+1,fa[u][0]=father;//继承父亲的信息 
        for(int i=1,p=lg[deep[u]]-1;i<=p;i++) fa[u][i]=fa[fa[u][i-1]][i-1];//预先处理节点2^k次方祖先 
        for(int i=head[u];i;i=edge[i].next)
            if(edge[i].to!=father)
                Pretreatment(edge[i].to,u);//处理向下的其它树枝 
    }
    int Lca(int from,int to)
    {
        if(deep[from]<deep[to]) Swap(from,to);//将from的深度设置为最深 
        while(deep[from]>deep[to]) from=fa[from][lg[deep[from]-deep[to]]-1];//倍增将x跳跃到与y同层 
        if(from==to) return from;//判断是否在同一个节点了 
        for(int i=lg[deep[from]]-1;i>=0;i--) if(fa[from][i]!=fa[to][i]) from=fa[from][i],to=fa[to][i];//倍增跳跃到LCA的下一层 
        return fa[from][0];//返回两者的LCA 
    }
    int main()
    {
        int from,to;
        scanf("%d%d%d",&n,&cnt,&star);
        int k=n-1;
        FORa(i,1,k) scanf("%d%d",&from,&to),Add_edge(from,to),Add_edge(to,from);
        FORa(i,1,n) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
        Pretreatment(star,0);//预处理,最重要的优化,倍增跳 
        FORa(i,1,cnt)
        {
            scanf("%d%d",&from,&to);
            printf("%d
    ",Lca(from,to));//求LCA 
        }
    } 
    /*
    5 5 4
    3 1
    2 4
    5 1
    1 4
    2 4
    3 2
    3 5
    1 2
    4 5
    */

    五、相关转载与推荐文章(十分感谢这些博主)

        倍增求lca(模板)

          题解 P3379 【【模板】最近公共祖先(LCA)】

  • 相关阅读:
    C++ Primer Plus章节编程练习(第十章)
    Bezier曲线
    C++静态持续变量
    计算机图形学之三维图形变换
    计算机图形学之二维图形变换
    C++ Primer Plus章节编程练习(第七章)
    C++中的指针与const
    Java 输入输出流
    Java Fx 画圆环
    注册事件及事件处理
  • 原文地址:https://www.cnblogs.com/SeanOcean/p/11294045.html
Copyright © 2011-2022 走看看