洛谷 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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章