【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.
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.
相关文章推荐
- 【RMQ&LCA】Nearest Common Ancest…
- 【RMQ&am 4000 p;amp;LCA】Nearest Common Ancest…
- 【RMQ&LCA】运动员的身高 解题报告…
- 【RMQ&LCA】Cartesian Tree(笛卡…
- 【RMQ&LCA】Distance Queries(距…
- 【RMQ&LCA】[Usaco2010 Hol]cowpol…
- POJ 1470 Closest Common Ancestors(LCA&RMQ)
- ZOJ 1141 Closest Common Ancestors(LCA)
- 杭电1159——Common Subsequence
- 【LCA】 POJ 1470 Closest Common Ancestors
- Common Trouble Solution--CASE NE…
- POJ-1470 Closest Common Ancestors【LCA】
- POJ 1470 Closest Common Ancestors [LCA+RMQ]
- (算法)Tarjan离线算法解决LCA问题 (附POJ 1470 Closest Common Ancestors 代码)
- Keep Sites Running Smoothly By Avoiding These 10 Common ASP.NET Pitfalls
- Closest Common Ancestors---poj1470(LCA+离线算法)
- POJ 1470 Closest Common Ancestors (LCA, dfs+ST在线算法)
- POJ-1470 Closest Common Ancestors【LCA】
- Closest Common Ancestors - POJ 1470 LCA
- POJ - 1470 Closest Common Ancestors (LCA tarjan)