Tarjan离线求LCA

上次貌似在处理RMQ问题时提起了倍增,这玩意可以求动态LCA(比较牛),现在我们在讲dfs时间戳的时候发现了一个求静态LCA的算法

何为时间戳?

在这里我们定义两个数组f和d,在深度优先搜索时记录这两个的值——f表示对这个店开始搜索的时间,d表示对这个店完成搜索的时间

我们假设所有操作花费的都是单位时间,就可以得到一棵树的完整时间戳啦。

这东西有什么用呢?

对于树上任意两个和它们的LCA间的时间戳关系

然后我们把所有询问都存下来

我们发现其实两个点的LCA就是一个最“下面”的左边包含u,右边包含v的点。

其实我们发现就是第一个还保持在u通往根的灰色状态的点(如果u,v属于同一个根的话)

所以我们在dfs时顺带处理一下第一个灰色节点就行了

模板题:POJ1330

#include<iostream>
#include<cstdio>
#include<algorithm>
const int N=10005;
using namespace std;
int n,i,u,v,a,b,rep[N],head[N],num,next[N],point[N],T;
bool flag[N],FLAG[N];
void make_way(int u,int v)
{
    point[++num]=v;
    next[num]=head[u];
    head[u]=num;
}
int getrep(int u)
{
    if(rep[u]==u)
    {
        return u;
    }
    return rep[u]=getrep(rep[u]);
}
void Init()
{
    scanf("%d",&n);
    num=0;
    for(int i=1;i<=n;i++)
    head[i]=0,rep[i]=i,flag[i]=FLAG[i]0;
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&u,&v);
        FLAG[v]=1;
        make_way(u,v);
    }
    scanf("%d %d",&a,&b);
}
int findrt()
{
    for(int i=1;i<=n;i++)
    if(!FLAG[i])
    {
        return i;
    }
}
void LCA(int u)
{
    for(int i=head[u];i;i=next[i])
    {
        LCA(point[i]);
        //printf("i:%d rep:%d",point[i],rep[point[i]]);
        rep[rep[point[i]]]=u;
    }
    flag[u]=1;
    if(a==u&&flag[b])
    {
        cout<<getrep(b)<<endl;
    }else
    {
        if(b==u&&flag[a])
        cout<<getrep(a)<<endl;
    }
            
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        Init();
        LCA(findrt());
    }
}    

 

posted @ 2017-06-30 10:31  dancer16  阅读(249)  评论(0编辑  收藏  举报
描述