LCA(最近公共祖先)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA(最近公共祖先)相关的知识,希望对你有一定的参考价值。

三种方法:

1.树链剖分(在上一篇代码中已经讲解得很详细,不再一一赘述)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<climits>
#include<ctime>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#include<iomanip>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
typedef long long LL;
inline int read(){
    int x=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9){
        x=x*10+ch-0;
        ch=getchar();
    }
    return x;
}
const int M=500001;
int n,m,root,cnt=0,head[M],to[M<<1],next[M<<1],son[M],siz[M],top[M],dep[M],fa[M];
void Insert(int u,int v){
    to[cnt]=v;
    next[cnt]=head[u];
    head[u]=cnt++;
    to[cnt]=u;
    next[cnt]=head[v];
    head[v]=cnt++;
}
void dfs1(int u){
    siz[u]=1;
    for(int i=head[u];i!=-1;i=next[i]){
        int v=to[i];
        if(v!=fa[u]){
            fa[v]=u;
            dep[v]=dep[u]+1;
            dfs1(v);
            siz[u]+=siz[v];
            if(!son[u]||siz[son[u]]<siz[v])son[u]=v;
        }
    }
}
void dfs2(int u){
    if(son[u]){
        top[son[u]]=top[u];
        dfs2(son[u]);
    }
    for(int i=head[u];i!=-1;i=next[i]){
        int v=to[i];
        if(v!=fa[u]&&v!=son[u]){
            top[v]=v;
            dfs2(v);
        }
    }
}
int lca(int x,int y){
    while(1){
        int tx=top[x],ty=top[y];
        if(tx==ty)return (dep[x]<dep[y]?x:y);
        if(dep[tx]<dep[ty])y=fa[ty];else x=fa[tx];
    }
}
int main(){
    n=read();m=read();root=read();
    memset(head,-1,sizeof(head));
    rep(i,1,n-1){
        int x=read(),y=read();
        Insert(x,y);
    }
    dep[root]=1;
    dfs1(root);
    top[root]=root;
    dfs2(root);
    while(m--){
        int x=read(),y=read();
        printf("%d\\n",lca(x,y));
    }
    return 0;
}

2.Tarjan(慎用!如果题目是按照树剖卡常数的话,则此算法会MLE(空间大小为树剖的两倍))

有个特别形象的讲解在这里:http://www.cnblogs.com/JVxie/p/4854719.html

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#include<climits>
#include<ctime>
#include<iomanip>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
inline int read(){
    int x=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9){
        x=x*10+ch-0;
        ch=getchar();
    }
    return x;
}

const int M=500001;
int n,m,root;
bool vis[M]={0};
int cnt=0,tot=0,f[M],ans[M],to[M<<1],head[M],next[M],q[M<<1],nt[M<<1],h[M];
void insert_edge(int x,int y){
    to[cnt]=y;
    next[cnt]=head[x];
    head[x]=cnt++;
    to[cnt]=x;
    next[cnt]=head[y];
    head[y]=cnt++;
}
void insert_query(int x,int y){
    q[tot]=y;
    nt[tot]=h[x];
    h[x]=tot++;
    q[tot]=x;
    nt[tot]=h[y];
    h[y]=tot++;
}
int Find(int x){
    if(f[x]==x)return x;
    else return f[x]=Find(f[x]);
}
void dfs(int fa,int u){
    for(int i=head[u];i!=-1;i=next[i]){
        int v=to[i];
        if(v!=fa)dfs(u,v);
    }
    for(int i=h[u];i!=-1;i=nt[i]){
        int v=q[i];
        if(vis[v])ans[i/2+1]=Find(v);
    }
    vis[u]=1;
    f[u]=fa;
}
            
int main(){
    n=read();m=read();root=read();
    memset(head,-1,sizeof(head));
    memset(h,-1,sizeof(h));
    rep(i,1,n)f[i]=i;
    rep(i,1,n-1){
        int x=read(),y=read();
        insert_edge(x,y);
    }
    rep(i,1,m){
        int x=read(),y=read();
        insert_query(x,y);
    }
    dfs(root,root);
    rep(i,1,m)printf("%d\\n",ans[i]);
    return 0;
}

 

以上是关于LCA(最近公共祖先)的主要内容,如果未能解决你的问题,请参考以下文章

二叉树最近公共祖先

二叉树最近公共祖先

最近公共祖先(LCA)问题

LCA —— 最近公共祖先

[转]LCA 最近公共祖先

LCA(最近公共祖先)