LCA问题的Tarjan 脱机最小公共祖先算法

去年暑假就了解这个问题了,当时听的是转换成RMQ问题来做,但是对于当时连写个背包都要抄别人的我来说,听的是一头雾水。之后又略了解了一点,但都是纸上谈兵。偶然看到http://blogold.chinaunix.net/u3/113538/showart.php?id=2212612的一段话:

“这个算法基于并查集和深度优先搜索。算法从根开始,对每一棵子树进行深度优先搜索,访问根时,将创建由根结点构建的集合,然后对以他的孩子结点为根的子树进行搜索,使对于 u, v 属于其某一棵子树的 LCA 询问完成。这时将其所有子树结点与根结点合并为一个集合。 对于属于这个集合的结点 u, v 其 LCA 必定是根结点。”

豁然开朗。又在纸上模拟几遍,基本有点理解了。具体的资料网上很多,就不多写了。

http://kmplayer.javaeye.com/blog/604518

http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html都有介绍,学习时参考了。

ACM题库里有不少LCA问题的变形或是裸的LCA,比如POJ1330,但是这让E文不好的我情何以堪?只好拿出以前做过的RQNOJ上的28,一道能暴力出解的题目练练手:

我的很丑陋的代码:

var
  visit:array[1..1000000] of boolean;
  t:array[1..1000000] of record l,next,x:longint; end;
  anc,f:array[1..1000000] of longint;
  i,j,k,u,v,n,m:longint;
  a,b:longint;

function find(i:longint):longint;
begin
  if f[i]<>i then
    f[i]:=find(f[i]);
  exit(f[i]);
end;

procedure union(i,j:longint);
begin
  f[find(i)]:=f[j];
end;

procedure lca(u:longint);
var
  j:longint;
begin
  f[u]:=u;
  anc[u]:=u;
    j:=t[u].l;
    while j<>0 do
    begin
      lca(j);
      union(u,j);
      anc[find(u)]:=u;
      j:=t[j].next;
    end;
  visit[u]:=true;
  if (u=a)and(visit[b]) then
  begin
    writeln(anc[find(b)]);
    halt;
  end;
  if (u=b)and(visit[a]) then
  begin
    writeln(anc[find(a)]);
    halt;
  end;
end;


begin
  readln(n);
  for i:=1 to n-1 do
  begin
    readln(u,v);
    if t[u].l=0 then
      t[u].l:=v
    else begin
      j:=t[u].l;
      while t[j].next<>0 do
        j:=t[j].next;
      t[j].next:=v;
    end;
  end;
  readln(a,b);
  lca(1);
end.

posted on 2011-03-28 22:40  oa414  阅读(724)  评论(0编辑  收藏  举报

导航