您的位置:首页 > 其它

【RMQ&LCA】Closest Common Ancest…

2015-02-02 19:25 225 查看
【RMQ&LCA】Closest Common
Ancestors(最近公共祖先)
Time
Limit:1000MS  Memory Limit:65536KTotal Submit:3 Accepted:1
Description
最近公共祖先(cca.pas/c/cpp)【问题描述】  输入一棵有根树和一系列顶点。对每对顶点(u,v),算出它们的最近公共祖先。一个结点可以是它自己的祖先。(如例1中2的祖先是2和5)Input
每组数据输入格式:第一行是一个整数n,表示顶点数,其中 n <= 900 ,接下来n行格式如下:vertex:(nr_of_successors) successor1 successor2 ...vertices是一个1到n的整数,表示顶点,nr_of_successors表示该顶点的儿子数,successor i(
1<=i<= nr_of_successors
)表示该顶点的各儿子然后是一个整数nr_of_pairs,表示要求的顶点对数(u v) (x y) ...输入包含至少一组数据包含一些无用的空格,空行等Output
对每个公共祖先程序输出这个祖先和以它为祖先的顶点对数。格式为: ancestor:times ,其中ancestor表示祖先,times表示以它为祖先的顶点对数。 
Sample Input
 

 
Sample Output
 

 
Hint
Huge input, scanf is recommended.注意输入,如果使用c语言的请用scanf本题数据不完整,请在本系统测试通过后到http://poj.org/problem?id=1470
提交完整测试!
Source
Southeastern Europe 2000
  ST算法抄解题报告:    
(1)   
/  \  (2)  
(7) / 
\    
\(3)  (4)  
(8)   
/   \ 
(5)    (6) Step
1:       按先序遍历整棵树,记下两个信息:结点访问顺序和结点深度.       如上图:       结点访问顺序是:  1 2 3 2 4 5 4 6 4 2 1 7 8 7
1  //共2n-1个值
       结点对应深度是:  0 1 2 1 2 3 2 3 2 1 0 1 2 1 0Step 2:       如果查询结点3与结点6的公共祖先,则考虑在访问顺序中
       
3第一次出现,到6第一次出现的子序列: 3 2
4 5 4 6.       这显然是由结点3到结点6的一条路径.       在这条路径中,深度最小的就是最近公共祖先(LCA).

       结点2是3和6的LCA.Step 3:       于是问题转化为,给定一个数组R,及两个数字i,j,如何找出
       数组R中从i位置到j位置的最小值..
        如上例,就是R[]={0,1,2,1,2,3,2,3,2,1,0,1,2,1,0}.       
i=2;j=7;       这个问题就是经典的RMQ问题.
        这里介绍一个比较简单的方法O(nlogn)预处理,O(1)回答每个询问.       
RMQ问题的预处理:       用一个数组d[i][j]表示数组R中,从i位置到i+2^j-1位置的最小值.       这个d[i][j]很容易dp得到.       
d[i][j+1]=min{d[i][j],d[i+2^j][j]};       空间:
O(nlogn)
时间:
O(nlogn).Step 4:       询问:       如果询问:从a到b里面最小的值..主要思路找两个长度是2^k的区间:       
[a,a+2^k-1]及[b-1-2^k,b]把区间[a,b]覆盖掉.
这很容易做到的,       只要:
2^k*2>= |b-a|.       于是a到b里面的最小值
= min
{d[a][k],d[b-2^k-1][k]} 这题的输入极为无聊,完全是在没事找事。幸好看到了网上大牛的方法,用个函数来专门读入数字,这样才不会很麻烦。也是第一次用集合。我一开始把‘num:set of char=['0'..'9'];’写在了
var
下面,编译过了。而交到题库上,就显示编译错误,幸而不是考试。  const num:set of char=['0'..'9']; a:array[0..10]of
longint=(1,2,4,8,16,32,64,128,256,512,1024);var m,n,nf,father,l,r:longint; f,d:array[0..2001]of longint; tree:array[1..1001,0..1001]of longint; mi:array[1..2001,0..11,1..2]of longint; tot:array[1..1001]of longint;function
getnum:longint;var x:longint; c:char;begin read(c); x:=0; while not (c in num) do read(c); while c in num do  begin  x:=x*10+ord(c)-48;  read(c);  end; exit(x);end;procedure
dfs(fa,de:longint);var i:longint;begin inc(f[0]); f[f[0]]:=fa; d[f[0]]:=de; for i:=1 to tree[fa,0] do begin  dfs(tree[fa,i],de+1); inc(f[0]); f[f[0]]:=fa; d[f[0]]:=de; end;end;procedure main;var i,b,t:longint;begin for i:=1 to nf do mi[i,0,1]:=d[i]; for i:=1 to nf do mi[i,0,2]:=f[i];for t:=1 to
trunc(ln(nf)/ln(2)) do  for b:=1 to nf-a[t]+1 do   if
mi[b,t-1,1]>mi[b+a[t-1],t-1,1] then
mi[b,t]:=mi[b+a[t-1],t-1]                                    
else mi[b,t]:=mi[b,t-1];end;procedure work;var i,t,ll,rr:longint;begin for i:=1 to nf do  if f[i]=l then begin ll:=i; break; end; for i:=1 to nf do  if f[i]=r then begin rr:=i; break; end;if rr<ll
then  begin  rr:=rr+ll;  ll:=rr-ll;  rr:=rr-ll;  end;t:=trunc(ln(rr-ll+1)/ln(2)); if mi[ll,t,1]>mi[rr-a[t]+1,t,1]
then inc(tot[mi[rr-a[t]+1,t,2]])                                
else inc(tot[mi[ll,t,2]]);end;procedure init;var c:char; i,j,x,y,s:longint; mark:array[1..1001]of boolean;begin fillchar(mark,sizeof(mark),0); n:=getnum; nf:=2*n+1; for i:=1 to n do  begin  x:=getnum;  s:=getnum;  for j:=1 to s do   begin   y:=getnum;   inc(tree[x,0]);   tree[x,tree[x,0]]:=y;   mark[y]:=true;   end;  end; for i:=1 to n do  if not mark[i] then  begin  father:=i;  break;  end;dfs(father,0); main;m:=getnum; for i:=1 to m do  begin  l:=getnum;  r:=getnum;  work;  end;for i:=1 to n do  if tot[i]<>0 then
writeln(i,':',tot[i]);end;begin while not seekeof do  begin  fillchar(f,sizeof(f),0);  fillchar(d,sizeof(d),0);  fillchar(tree,sizeof(tree),0);  fillchar(mi,sizeof(mi),0);  fillchar(tot,sizeof(tot),0);  init; end;end. Tarjan算法抄解题报告:Tarjan算法基于深度优先搜索的框架,对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。 //Tarjanconst num:set of char=['0'..'9'];var m,n,father,l,r:longint; tree,q:array[1..1001,0..1001]of longint; tot,fa:array[0..1001]of longint; mark:array[1..1001]of boolean;function
getnum:longint;var x:longint; c:char;begin read(c); x:=0; while not (c in num) do read(c); while c in num do  begin  x:=x*10+ord(c)-48;  read(c);  end; exit(x);end;function
getfather(x:longint):longint;begin if fa[x]=x then exit(x) else  begin  fa[x]:=getfather(fa[x]);  exit(fa[x]);  end;end;procedure lca(x:longint);var i:longint;begin fa[x]:=x; for i:=1 to tree[x,0] do  begin  lca(tree[x,i]);  fa[tree[x,i]]:=x;  end; mark[x]:=true; for i:=1 to q[x,0] do  if mark[q[x,i]] then
inc(tot[getfather(q[x,i])]);end;procedure init;var c:char; i,j,x,y,s:longint;begin n:=getnum; for i:=1 to n do  begin  x:=getnum;  s:=getnum;  for j:=1 to s do   begin   y:=getnum;   inc(tree[x,0]);   tree[x,tree[x,0]]:=y;   mark[y]:=true;   end;  end; m:=getnum; for i:=1 to m do  begin  l:=getnum;  r:=getnum;  inc(q[l,0]);  q[l,q[l,0]]:=r;  inc(q[r,0]);  q[r,q[r,0]]:=l;  end; for i:=1 to
n do  if not mark[i] then  begin  father:=i;  break;  end; fillchar(mark,sizeof(mark),0); lca(father); for i:=1 to
n do  if tot[i]<>0 then
writeln(i,':',tot[i]);end;begin assign(input,'a.in'); reset(input); while not seekeof do  begin  fillchar(fa,sizeof(fa),0);  fillchar(tree,sizeof(tree),0);  fillchar(q,sizeof(q),0);  fillchar(tot,sizeof(tot),0);  fillchar(mark,sizeof(mark),0);  init; end; close(input);end. 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: