倍增求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\n",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)】

 

 

posted @ 2019-08-03 10:46  SeanOcean  阅读(365)  评论(0编辑  收藏  举报