洛谷 P3379 最近公共祖先 【LCA】

题目来源:https://www.luogu.org/problem/P3379
在这里插入图片描述
在这里插入图片描述
★第一道LCA题,看了半天,这是一道入门滴模板题


LCA有很多解法,本题仅用 倍增法求LCA (我目前只会这一种

思路:

仔细地看样例,可以看明白什么是LCA了吧。

先说说直观的暴力思路:

每个点有一个dep,代表 深度 。现在要求 LCA(a,b ,那么如果 dep(a)<dep(b) ,则交换 a和b(保证 dep[a]>=dep[b] )。然后a往上跳(a=a的祖先),一直到 dep[a]=dep[b] 最后一起往上跳,直到他们的祖先相同。

优化方法:

如果我们在跳的过程中可以 一次跳多步 ,那效率肯定会高很多,这就是 树上倍增法
首先 fa[ i ][ j ] 表示 i 的 第 2j 辈的祖先(从i往根节点的方向走 2j 步 到达的点,若不存在则为0),可知fa[ i ][ 0 ]表示父节点。
可以得到递推式 fa[ i ][ j ]=fa[ fa[ i ][ j-1 ] ][ j-1 ] 由此 可以从根节点推出全部的值

预处理部分差不多就是这样~
还想优化的话 可以加一个 lg数组 lg[ i ] 代表 log2(i)+1 递推如右:lg[i]=lg[i-1]+(1<<lg[i-1]==i);
详见代码
其他方法 如 tarjan rmq 可见大佬博客:https://blog.csdn.net/ywcpig/article/details/52336496


代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=5e5+5;
const int sz=1<<16;
const int inf=2e9;
const int mod=1e9+7;
const double pi=acos(-1);
typedef long long LL;
int n,m,cnt;
struct node
{
    int next;
    int to;
}edge[maxn<<1];
int head[maxn],dep[maxn];
int fa[maxn][22],lg[maxn];
template<class T>
inline void read(T &x)
{
    char c;x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
void add_edge(int a,int b)              //链式前向星
{
    edge[++cnt].to=b;
    edge[cnt].next=head[a];
    head[a]=cnt;
}
void dfs(int p,int f)                           //预处理
{
//    cout<<p<<'x'<<endl;
    dep[p]=dep[f]+1;
    fa[p][0]=f;
    for(int i=1;(1<<i)<=dep[p];i++){
        fa[p][i]=fa[fa[p][i-1]][i-1];
    }
    for(int i=head[p];i;i=edge[i].next){          
        if(edge[i].to!=f)                         //不能等于父节点
        dfs(edge[i].to,p);
    }
}
int lca(int a,int b)
{
    if(dep[a]<dep[b]) swap(a,b);            //保证 a深度大
    while(dep[a]>dep[b]){                  //a往上跳
        a=fa[a][lg[dep[a]-dep[b]]-1];
    }
    if(a==b) return a;                    
    for(int i=lg[dep[a]]-1;i>=0;i--){     
        if(fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];      //a b 一起往上跳
    }
    return fa[a][0];                 //注意返回的是 a的父节点 
}
int main()
{
    int s,a,b;
    cnt=0;
    read(n); read(m); read(s);
    for(int i=1;i<n;i++){
        read(a); read(b);
        add_edge(a,b);
        add_edge(b,a);
    }
    dfs(s,0);
    for(int i=1;i<=n;i++)
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    while(m--){
        read(a); read(b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

发布了71 篇原创文章 · 获赞 89 · 访问量 8558

猜你喜欢

转载自blog.csdn.net/weixin_43890662/article/details/99575953