最近公共祖先【LCA】 模板

LCA即最近公共祖先,是指 :在有根树中,找出某两个结点u和v最近的公共祖先。

时间复杂度O(nlogn+m+n)

步骤:
1.将树看作一个无向图,从根节点开始深搜,得到一个遍历序列。 
2.在x~y区间中利用RMQ算法找到深度最小返回其下标。

可以上洛谷找模板题测试:https://www.luogu.org/problemnew/show/P3379

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=500010;

struct node
{
    int v,ne;
}q[N*2];

int f[N],vis[N],deep[N*2],pos[N*2];
int dp[N*2][21];
int cut,n,e;

void add(int a,int b)
{
    q[++e].v=b;
    q[e].ne=f[a];
    f[a]=e;
    return;
}

void dfs(int x,int s,int pre)
{
    vis[x]=++cut;
    pos[cut]=x;
    deep[cut]=s;
    for(int i=f[x];i!=0;i=q[i].ne)
    {
        int v=q[i].v;
        if(v==pre) continue;
        dfs(v,s+1,x);
        pos[++cut]=x;
        deep[cut]=s;
    }
    return;
}

void RMQ()
{
    for(int i=1;i<=cut;i++)
        dp[i][0]=i;
    for(int i=1;(1<<i)<=cut;i++)
    {
        for(int j=1;j+(1<<i)-1<=cut;j++)
        {
            int a=dp[j][i-1];
            int b=dp[j+(1<<(i-1))][i-1];
            if(deep[a]<=deep[b])
                dp[j][i]=a;
            else dp[j][i]=b;
        }
    }
    return;
}

int query(int x,int y)
{
    int k=0;
    k=log2(double(y-x+1));
    int a=dp[x][k],b=dp[y-(1<<k)+1][k];
    if(deep[a]<=deep[b])
        return pos[a];
    else
        return pos[b];
    return 0;
}

int main()
{
    cut=0;e=0;
    int m,x;
    scanf("%d %d %d",&n,&m,&x);
    int a,b;
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(x,0,-1);
    RMQ();
    for(int i=0;i<m;i++)
    {
        scanf("%d %d",&a,&b);
        printf("%d\n",query(min(vis[a],vis[b]),max(vis[a],vis[b])));
    }
    return 0;
}

倍增法求LCA:
复杂度 :预处理O(nlogn),每次询问O(logn)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=5e5+10;

struct node
{
    int v,ne;
}q[N*2];

int deep[N],par[N][20],f[N],vis[N];
int cut,e;

void add(int a,int b)
{
    q[++e].v=b;
    q[e].ne=f[a];
    f[a]=e;
}

void dfs(int x,int pre,int s)
{
    deep[x]=s;
    for(int i=f[x];i!=0;i=q[i].ne)
    {
        int v=q[i].v;
        if(v==pre) continue;
        par[v][0]=x;
        dfs(v,x,s+1);
    }
}

void prepare(int n)
{
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i<=n;i++)
        {
            if(par[i][j-1]!=-1)
                par[i][j]=par[par[i][j-1]][j-1];
        }
    }
}

int lca(int a,int b)
{
    if(deep[a]<deep[b]) swap(a,b);
    int k=trunc(log2(deep[a]));
    for(int i=k;i>=0;i--)
    {
        if(deep[a]-(1<<i)>=deep[b])
            a=par[a][i];
    }
    if(a==b) return a;
    for(int i=k;i>=0;i--)
    {
        if(par[a][i]!=par[b][i]&&par[a][i]!=-1)
        {
            a=par[a][i];
            b=par[b][i];
        }
    }
    return par[a][0];
}

int main()
{
    memset(par,-1,sizeof(par));
    int n,m,x,a,b;
    scanf("%d %d %d",&n,&m,&x);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(x,0,0);
    prepare(n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41984014/article/details/89320235