【动态树初探】link-cut tree
2011-09-05 10:50
399 查看
转载自:
很愚昧的以为动态树是一种数据结构,现在才知道动态树是是一类问题(Dynamic Tree Problems)。
spoj上有一系列关于树的很有趣的题目(qtree1~4),和树链剖分、动态树有关,所以就狠下心的研究了一番,多亏找到了一篇好论文《qtree解法的一些研究 by yangzhe》,解决动态树问题的数据结构叫link-cut tree(又是tarjan发明的,无限崇拜!!),看懂了思想以后便抱着视死如归的心态编了起来(拿hnoi2010的bounce来练手),因为看了标程,7kb、8kb、7kb,=、=。
首先说说树链剖分,差不多就是link-cut tree 的静态版(静态树!?),前提是树的形状不发生改变。树中的重边的定义就是每个点向他最大的儿子(子树大小最大)连的边,其他的边就是轻边。可以证明一个点到根节点经过的轻边数不超过logN,证明:
定义size(i)为以i为根的子树大小,
若 i 和 fa(i) 之间连的是轻边
也就是 存在 j∈son(fa(i)),size(j)≥size(i)
又∵ size(fa(i))≥size(i)+size(j),
∴size(fa(i))>2*size(i).
每走一条轻边节点数都会增大一倍,那当然不可能超过logN条轻边咯。
对于一串连续的重边,我们就把它压缩为一条重路径。
如果这棵树的形状不改变,那么重路径自然也不会发生变化,那我们可以用线段树或者是虚二叉树来维护,若会改变,则用splay tree维护(以深度为关键字)。
在实际link-cut tree的编写中并不要考虑谁是重边谁是轻边,假设你访问了某一个点,就把它到父亲的边全改为重边就可以了,可以证明也是logN的(证明有点懵。。)。
回顾hnoi2010的弹飞绵羊,这正是一个动态树问题(哎,我靠怎么那时候不会捏?),要求支持这样两个操作:
1.询问某个点的深度。
2.改变某个点的父亲。
记得之前用块状链表水过了,速度还不错,不过看来 link-cut tree 还是以绝对优势压倒了:
块状链表:
link-cut tree
这次还给了我一个教训,不要被标程吓到了。。。可能牛们为了让人更容易看懂才编长一点的吧,却着实把我这种小白吓到了。。。
自己编起来感觉也很舒服,74行,主要注意的还是旋转时候的父亲的转化。
(裸的splay不解释。。。都是想想写写胡编乱抽的)代码:
view
plain
program link_cut_tree; const maxn=200005;
var
root:array[0..maxn] of boolean;
l,r,fa,s:array[0..maxn] of longint;
n,i,task,j,z,k:longint;
procedure left(i:longint); begin
z:=r[i];r[i]:=l[z];fa[r[i]]:=i;l[z]:=i;
if i=l[fa[i]] then l[fa[i]]:=z else
if i=r[fa[i]] then r[fa[i]]:=z; //!!
fa[z]:=fa[i];fa[i]:=z;
root[z]:=root[i] xor root[z];
root[i]:=root[i] xor root[z];
s[i]:=s[l[i]]+s[r[i]]+1;
s[z]:=s[l[z]]+s[r[z]]+1;
end;
procedure right(i:longint); begin
z:=l[i];l[i]:=r[z];fa[l[i]]:=i;r[z]:=i;
if i=l[fa[i]] then l[fa[i]]:=z else
if i=r[fa[i]] then r[fa[i]]:=z; //!!
fa[z]:=fa[i];fa[i]:=z;
root[z]:=root[i] xor root[z];
root[i]:=root[i] xor root[z];
s[i]:=s[l[i]]+s[r[i]]+1;
s[z]:=s[l[z]]+s[r[z]]+1;
end;
procedure splay(i:longint); begin
while not root[i] do
if i=l[fa[i]] then right(fa[i]) else left(fa[i]);
end;
procedure access(i:longint); begin
splay(i);
while fa[i]<>0 do begin
splay(fa[i]);
z:=fa[i];
root[r[z]]:=true;root[i]:=false;
r[z]:=i;
s[z]:=s[l[z]]+s[r[z]]+1;
splay(i);
end;
end;
begin
assign(input,'input.txt');reset(input);
assign(output,'output.txt');rewrite(output);
readln(n);
for i:=1 to n do begin
read(fa[i]);inc(fa[i],i);
if fa[i]>n then fa[i]:=n+1;
end;
for i:=1 to n+1 do s[i]:=1;
fillchar(root,sizeof(root),true);
readln(task);
for task:=1 to task do begin
read(z);
if z=1 then begin
readln(i);inc(i);
access(i);
writeln(s[l[i]]);
end else begin
readln(j,k);inc(j);
splay(j);
fa[l[j]]:=fa[j];
root[l[j]]:=true;
l[j]:=0;s[j]:=s[r[j]]+1;
fa[j]:=j+k;
if fa[j]>n then fa[j]:=n+1;
end;
end;
close(input);close(output);
end.
Evil.livE
很愚昧的以为动态树是一种数据结构,现在才知道动态树是是一类问题(Dynamic Tree Problems)。spoj上有一系列关于树的很有趣的题目(qtree1~4),和树链剖分、动态树有关,所以就狠下心的研究了一番,多亏找到了一篇好论文《qtree解法的一些研究 by yangzhe》,解决动态树问题的数据结构叫link-cut tree(又是tarjan发明的,无限崇拜!!),看懂了思想以后便抱着视死如归的心态编了起来(拿hnoi2010的bounce来练手),因为看了标程,7kb、8kb、7kb,=、=。
首先说说树链剖分,差不多就是link-cut tree 的静态版(静态树!?),前提是树的形状不发生改变。树中的重边的定义就是每个点向他最大的儿子(子树大小最大)连的边,其他的边就是轻边。可以证明一个点到根节点经过的轻边数不超过logN,证明:
定义size(i)为以i为根的子树大小,
若 i 和 fa(i) 之间连的是轻边
也就是 存在 j∈son(fa(i)),size(j)≥size(i)
又∵ size(fa(i))≥size(i)+size(j),
∴size(fa(i))>2*size(i).
每走一条轻边节点数都会增大一倍,那当然不可能超过logN条轻边咯。
对于一串连续的重边,我们就把它压缩为一条重路径。
如果这棵树的形状不改变,那么重路径自然也不会发生变化,那我们可以用线段树或者是虚二叉树来维护,若会改变,则用splay tree维护(以深度为关键字)。
在实际link-cut tree的编写中并不要考虑谁是重边谁是轻边,假设你访问了某一个点,就把它到父亲的边全改为重边就可以了,可以证明也是logN的(证明有点懵。。)。
回顾hnoi2010的弹飞绵羊,这正是一个动态树问题(哎,我靠怎么那时候不会捏?),要求支持这样两个操作:
1.询问某个点的深度。
2.改变某个点的父亲。
记得之前用块状链表水过了,速度还不错,不过看来 link-cut tree 还是以绝对优势压倒了:
块状链表:
link-cut tree
这次还给了我一个教训,不要被标程吓到了。。。可能牛们为了让人更容易看懂才编长一点的吧,却着实把我这种小白吓到了。。。
自己编起来感觉也很舒服,74行,主要注意的还是旋转时候的父亲的转化。
(裸的splay不解释。。。都是想想写写胡编乱抽的)代码:
view
plain
program link_cut_tree; const maxn=200005;
var
root:array[0..maxn] of boolean;
l,r,fa,s:array[0..maxn] of longint;
n,i,task,j,z,k:longint;
procedure left(i:longint); begin
z:=r[i];r[i]:=l[z];fa[r[i]]:=i;l[z]:=i;
if i=l[fa[i]] then l[fa[i]]:=z else
if i=r[fa[i]] then r[fa[i]]:=z; //!!
fa[z]:=fa[i];fa[i]:=z;
root[z]:=root[i] xor root[z];
root[i]:=root[i] xor root[z];
s[i]:=s[l[i]]+s[r[i]]+1;
s[z]:=s[l[z]]+s[r[z]]+1;
end;
procedure right(i:longint); begin
z:=l[i];l[i]:=r[z];fa[l[i]]:=i;r[z]:=i;
if i=l[fa[i]] then l[fa[i]]:=z else
if i=r[fa[i]] then r[fa[i]]:=z; //!!
fa[z]:=fa[i];fa[i]:=z;
root[z]:=root[i] xor root[z];
root[i]:=root[i] xor root[z];
s[i]:=s[l[i]]+s[r[i]]+1;
s[z]:=s[l[z]]+s[r[z]]+1;
end;
procedure splay(i:longint); begin
while not root[i] do
if i=l[fa[i]] then right(fa[i]) else left(fa[i]);
end;
procedure access(i:longint); begin
splay(i);
while fa[i]<>0 do begin
splay(fa[i]);
z:=fa[i];
root[r[z]]:=true;root[i]:=false;
r[z]:=i;
s[z]:=s[l[z]]+s[r[z]]+1;
splay(i);
end;
end;
begin
assign(input,'input.txt');reset(input);
assign(output,'output.txt');rewrite(output);
readln(n);
for i:=1 to n do begin
read(fa[i]);inc(fa[i],i);
if fa[i]>n then fa[i]:=n+1;
end;
for i:=1 to n+1 do s[i]:=1;
fillchar(root,sizeof(root),true);
readln(task);
for task:=1 to task do begin
read(z);
if z=1 then begin
readln(i);inc(i);
access(i);
writeln(s[l[i]]);
end else begin
readln(j,k);inc(j);
splay(j);
fa[l[j]]:=fa[j];
root[l[j]]:=true;
l[j]:=0;s[j]:=s[r[j]]+1;
fa[j]:=j+k;
if fa[j]>n then fa[j]:=n+1;
end;
end;
close(input);close(output);
end.
相关文章推荐
- 【动态树初探】link-cut tree
- 【学习心得】Link-cut Tree
- Link-Cut Tree
- Link-Cut Tree
- 【Learning】Link-Cut Tree 题目汇总
- LCT(Link Cut Tree)动态树学习笔记
- LCT(Link-Cut Tree)学习笔记
- 【BZOJ2959】长跑(Link-Cut Tree,并查集)
- HDU 2475 BOX 动态树 Link-Cut Tree
- HDOJ 题目4010 Query on The Trees(Link Cut Tree连接,删边,路径点权加,路径点权最大值)
- bzoj 1180(link cut tree)
- Link-Cut Tree
- bzoj 2002 HNOI 2010 弹飞绵羊 bounce [Link-Cut Tree]
- 动态树之LCT(link-cut tree)讲解
- Link-Cut Tree(Insider Preview)
- 动态树(Link-Cut Tree)学习小结
- bzoj2049 [Sdoi2008]Cave 洞穴勘测 (Link Cut Tree)
- 【BZOJ4736】温暖会指引我们前行(Link-Cut Tree)
- 专题总结:动态树 LCT(Link cut tree)
- [Noi2014]魔法森林 (Link Cut Tree)