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.