LCA(最近公共祖先)

前提引入:

看到LCA這個詞的中文意思,顧名思義,也就是找兩個點最近的公共祖先罷了。
我相信,其實不看LCA的代碼,就自己寫,也能寫出LCA,因爲LCA算法實現就是兩個點一步一步往上走,找到同一個點就輸出結束
但這樣太慢了!O(n),我們需要優化!
所以智慧的科學家們發明出了許許多多玄♂ 學的優化方法,在這裏,我們需要掌握倍增(在線)和tarjan(離線)兩種優化方法

倍增:

顧名思義,倍增就是一步跳很多格,不一格一格跳了,而是一段一段地跳。怎樣利用倍增來求LCA呢?這裏我們先要做一些熱身準備:
1.d[i]:表示點i所在的層數
2.f[i][j]:表示i跳2的j次方後到達的點
3.f[i][j]=f[f[i][j-1]][j-1]  一個點跳兩段到達的地方等於先跳一段,再跳一段
瞭解了倍增現實所需要的數組和遞推式,我們就來實現它吧!

”””””””””””’

倍增三部曲:
1.dfs層數搜索曲
2.倍增預處理曲
3.lca登場尋找答案曲

”””””””””””’

我們以一道模版題來講解洛谷P3379模版題

#include <iostream>
#include <cstdio>
using namespace std;

//Statement_common
struct Edge{int next,to;}edge[500000*2+1];
int n,q,s,num_edge,x,y;
int head[500001],d[500001];
int f[500001][31];
//Statement_fun
int lca(int x,int y);
void dfs(int x);
void add_edge(int from,int to);
int read();

int main()
{
    cin>>n>>q>>s;
    for(int i=1;i<=n-1;i++)
    {
        x=read(),y=read();
        add_edge(x,y),add_edge(y,x);
    }
    //1.dfs層數搜索曲
    d[s]=1,f[s][0]=0;
    dfs(s);
    //2.倍增預處理曲
       for(int j=1;j<=20;j++)   //注意:最外層枚舉步數!
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    //3.lca登場尋找答案曲
    for(int i=1;i<=q;i++)
    {
        x=read(),y=read();
        printf("%d\n",lca(x,y));
    }
    return 0;
}

int lca(int x,int y)
{
    if(d[x]<d[y]) swap(x,y);//人爲規定x層數深於y
    //以下一重循環把x提到與y同一層
    for(int i=20;i>=0;i--)
        if(d[f[x][i]]>=d[y])
            x=f[x][i];
    if(x==y) return x;//如果相等,不用再找了
    //以下一重循環找lca
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];//因爲最後找到的是它們的lca的下一層,所以要輸出爸爸
}

void dfs(int x)//dfs找層數不解釋
{
    for(int i=head[x];i!=0;i=edge[i].next)
        if(!d[edge[i].to])
        {
            d[edge[i].to]=d[x]+1;//由點x拓展出來的點的層數肯定是x層數+1
            f[edge[i].to][0]=x;//dfs時順便找出兩點間關係
            dfs(edge[i].to);
        }
}

void add_edge(int from,int to)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    head[from]=num_edge;
}

int read()
{
    int x=0; char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    return x;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章