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.

你可能感兴趣的:(tar)