LCA的tarjan算法--总结

4 篇文章 0 订阅
3 篇文章 0 订阅

LCA问题,即最近公共祖先问题,有很多种解法,其中比较高效的有在线的转化为ST表的算法、树上倍增算法,和离线的tarjan算法。在线的算法都很简单,这里要讲的是离线的tarjan算法。

思想

tarjan算法其实也并不难理解,它的主要思想就是利用了DFS的深度优先的顺序,算法的主框架就是一个DFS遍历,同时利用了并查集的快速合并。理解的时候可以对DFS的过程进行拆分,把访问一个节点过程拆分为访问它和退出访问,退出访问即回溯,由于DFS的顺序是每一条路径都走到底,然后往回退一步,再访问上一层的其他儿子,再退,再访问上一层的上一层的其他儿子……,并查集中的fa数组记录的就是每一个节点当前往回回溯到哪一个点。对于每个节点,访问和它有关的每一个询问,如果询问中的另一个节点已经访问过了,那么这一次询问的答案就是另一个节点的fa(当前它往回回溯到的节点)。每搜索了一个节点就要将它的fa与父节点进行合并(注意合并的顺序)。

代码

下面给出poj1330的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 10006
using namespace std;
int tet,n,tot,ans,Root,X,Y,fa[maxn],lnk[maxn],son[maxn],nxt[maxn];
bool vis[maxn],isR[maxn];
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
void add(int x,int y){
    nxt[++tot]=lnk[x];son[tot]=y;lnk[x]=tot;
}
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
void merge(int x,int y){
    x=get(x);y=get(y);
    if(x!=y)fa[x]=y;
}
void tarjan(int x){
    vis[x]=0;
    for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]) tarjan(son[j]),merge(son[j],x);
    if(x==X&&!vis[Y])ans=get(Y);
    if(x==Y&&!vis[X])ans=get(X);
}
int main(){
    freopen("lca.in","r",stdin);
    freopen("lca.out","w",stdout);
    tet=_read();
    while(tet--){
        tot=0;memset(lnk,0,sizeof(lnk));memset(vis,1,sizeof(vis));memset(isR,1,sizeof(isR));
        n=_read();
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1,x,y;i<n;i++)x=_read(),y=_read(),add(x,y),isR[y]=0;
        X=_read();Y=_read();
        for(int i=1;i<=n;i++) if(isR[i])Root=i;
        tarjan(Root);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值