Tarjan之求LCA

Posted ck666

tags:

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

Tarjan之求LCA

不要问我为什么写完Tarjan还要再补一句“求LCA的那个”

因为只说Tarjan的话完全不知道你指的是哪个算法……

劳模Tarjan同志证明了好多算法,而且全都叫Tarjan算法(是不会起名了吗x)

这个Tarjan是一个求LCA的离线算法

关于什么是在线什么是离线……

所谓的在线算法就是实时性的,比方说,给你一个输入,算法就给出一个输出,就像是http请求,请求网页

一样。给一个实时的请求,就返回给你一个请求的网页。而离线算法则是要求一次性读入所有的请求,然后在

统一处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程

中是被先处理的。”

↑从网上抄来的w↑感觉很好懂……

因为是离线做法,重新排列了一下解决询问的顺序,所以要比在线算法快

Tarjan的前置技能是dfs和并查集,不会的赶紧去学x

那么正片开始!

因为这是个离线算法,所以读进来各条边之后继续读进来所有询问并存起来

然后从根开始dfs

访问到每个点的时候,先遍历从这个点u出去的所有边,dfs未访问过的点v,访问后把这个点并到u的并查集上

然后扫一遍和u有关的所有询问及其涉及到的点v

如果点v已经被访问过了,那么说明u和v的LCA已经算好了

真相只有一个,LCA就是点v所在的并查集的祖先

赶紧存进答案数组里……

全部dfs完成后,答案也全部算完了,按询问序输出答案就OK了

具体实现可以看代码OvO

 

技术分享
//Tarjan lca 
#include<iostream>
#include<cstdio>
using namespace std;
const int N=500009;
int n,m,s,cnt,tot,ep[N],qp[N],fa[N];
bool vis[N];
struct edge{
       int to,nex;
}e[N<<1];
struct query{
       int to,nex,ans;
}q[N<<1];
int read()
{
    int an=0;
    char ch=getchar();
    while(!(0<=ch&&ch<=9))ch=getchar();
    while(0<=ch&&ch<=9){an=an*10+ch-0;ch=getchar();}
    return an;
}
int found(int x)
{
    if(x==fa[x])return x;
    return fa[x]=found(fa[x]);
}
void adde(int u,int v)
{
     ++cnt;
     e[cnt].to=v;
     e[cnt].nex=ep[u];
     ep[u]=cnt;
}
void addq(int u,int v)
{
     ++tot;
     q[tot].to=v;
     q[tot].nex=qp[u];
     qp[u]=tot;
}
void dfs(int u)
{
     vis[u]=1;fa[u]=u;
     for(int i=ep[u];i;i=e[i].nex)
     {
       int v=e[i].to;
       if(!vis[v]){dfs(v);fa[v]=u;}
     }
     for(int i=qp[u];i;i=q[i].nex)
     {
       int v=q[i].to;
       if(vis[v])
       {
         q[i].ans=found(v);
         if(i&1)q[i+1].ans=q[i].ans;
         else   q[i-1].ans=q[i].ans;
       }
     }
}
int main()
{
    n=read(),m=read(),s=read();
    for(int i=1;i<n;++i)
    {
      int x=read(),y=read();
      adde(x,y);adde(y,x);
    }
    for(int i=1;i<=m;++i)
    {
      int x=read(),y=read();
      addq(x,y);addq(y,x);
    }
    dfs(s);
    for(int i=1;i<=m;++i)
    printf("%d\n",q[i<<1].ans);
    return 0;
}
Tarjan LCA(wypx)

 

偷着放一份s菌的代码

技术分享
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<algorithm>
using namespace std;
int read(){
    int an=0,f=1;char ch=getchar();
    while(!(0<=ch&&ch<=9)){if(ch==-)f=-f;ch=getchar();}
    while(0<=ch&&ch<=9){an=an*10+ch-0;ch=getchar();}
    return an*f;
}
int n,m,s,cnt,qf[500099],f[500099],fa[500099],ans[500099<<1];
int x,y;
bool vis[500099];
int found(int x){
    if(fa[x]!=x)fa[x]=found(fa[x]);
    return fa[x];}
struct saber{
int to,nex;
}b[500099<<1];
struct question{
int to,nex;
}qu[500099<<1];
void add(int x,int y){
    cnt++;b[cnt].to=y;
    b[cnt].nex=f[x];f[x]=cnt;
}
void add(int x,int y,int z){
    qu[z].to=y;
    qu[z].nex=qf[x];
    qf[x]=z;
}
void dfs(int x){
    fa[x]=x;vis[x]=1;//cout<<x<<" ";
    for(int i=f[x];i;i=b[i].nex){
        int v=b[i].to;
        if(!vis[v]){
            dfs(v);
            fa[v]=x;
        }
    }
    for(int i=qf[x];i;i=qu[i].nex){
        int v=qu[i].to;
        if(vis[v]){
            ans[i]=found(v);
            if(i&1)ans[i+1]=ans[i];
            else ans[i-1]=ans[i];
        }
    }
}
int main(){
    n=read();m=read();s=read();
    for(int i=1;i<n;i++){
        int x,y;x=read();y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=m;i++){
        x=read();y=read();
        add(x,y,i*2-1);add(y,x,i*2);
    }
    dfs(s);
    for(int i=1;i<=m;i++){
        cout<<ans[2*i]<<endl;}
    return 0;
}
Tarjan LCA (s_a_b_e_r)

by:wypx

 

以上是关于Tarjan之求LCA的主要内容,如果未能解决你的问题,请参考以下文章

Closest Common Ancestors (Lca,tarjan)

POJ 1470 Closest Common Ancestors (模板题)(Tarjan离线)LCA

Tarjan 算法求 LCA / Tarjan 算法求强连通分量

poj3694(lca + tarjan求桥模板)

POJ 3694 (tarjan缩点+LCA+并查集)

POJ 1236 Network of Schools(tarjan算法 + LCA)