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

猜你喜欢

转载自blog.csdn.net/qq_39824819/article/details/81743837