POJ 1330 LCA最近公共祖先 离线tarjan算法

题意要求一棵树上,两个点的最近公共祖先 即LCA

现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出

原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10000+10;
int f[N],pre[N],vis[N];
int ans[N];
vector<int> v[N];
int n,S,T;
void init()
{
    for (int i=0;i<=n+1;i++){
        f[i]=i;
        v[i].clear();
        vis[i]=0;
        pre[i]=-1;
    }
}
int findset(int x)
{
    if (x!=f[x]){
        f[x]=findset(f[x]);
    }
    return f[x];
}
int unionset(int a,int b)
{
    int r1=findset(a);
    int r2=findset(b);
    if (r1==r2) return r1;
    f[r2]=r1;
    return r1;
}
void LCA(int u)
{
    ans[u]=u;

    for (int i=0;i<v[u].size();i++){
        int vx=v[u][i];
        LCA(vx);
        int rt=unionset(u,vx);
        ans[rt]=u;
    }
    vis[u]=1;
    if (u==S){
        if (vis[T]){
            printf("%d\n",ans[findset(T)]);
            return;
        }
    }
    else
    if (u==T){
        if (vis[S]){
            printf("%d\n",ans[findset(S)]);
            return;
        }
    }

}
int main()
{
    int t,a,b;
    scanf("%d",&t);
    while (t--){
      scanf("%d",&n);
      init();
      for (int i=1;i<n;i++){
          scanf("%d%d",&a,&b);
          v[a].push_back(b);
          pre[b]=a;
      }
      scanf("%d%d",&S,&T);
      for (int i=1;i<=n;i++){
          if (pre[i]==-1){
              LCA(i);
              break;
          }
      }
    }
    return 0;
}

  

posted @ 2014-08-10 15:08  KRisen  阅读(274)  评论(0编辑  收藏  举报