Link-Cut-Tree

 

Link-Cut-Tree
wcz¹
December31,2017
¹Contactme:aiyoupass@outlook.com
 
Contents
1DynamicTreeProblems[2]2
2Link-Cut-Trees[3]3
2.1Build..................................3
2.2Access.................................6
2.3Isroot..................................6
2.4Findroot................................6
2.5Beroot.................................6
2.6Split...................................6
2.7Merge..................................7
2.8Code..................................7
3Pathoperation*[1]8
3.1Example1...............................9
4Others9
4.1Connect................................9
1
 
1DynamicTreeProblems[2]
动态树问题,即要求我们维护一个由若干棵子结点无序的有根树组成的
森林.要求这个数据结构支持对树的分割,合并,对某个点到它的根的路径的
某些操作,以及对某个点的子树进行的某些操作.
维护一个包含N个点的森林,并且支持形态和权值信息的操作.
(1).形态信息
(a).link(u,v)–添加边(u,v).
(b).cut(u,v)–删除边(u,v).
(c).find(u)–找到u所在的树.
(2).权值信息
(a).路径操作:对一条简单路径上的所有对象进行操作.
(b).树操作:对一棵树内的所有对象进行操作.
现有数据结构,
•EulerTourTrees¹
•ST-Trees²
•Top-Trees³
这几种数据结构都存在一定的局限性,因此动态树问题并没有被完全解
.
在信息学奥赛中,我们常常会遇到动态树的简化问题.我们涉及的操作只
有对树形态的操作和对于路径的操作.因此就有一种解决动态树问题的数据
结构
LinkcutTrees
¹不支持路径操作
²不支持树权操作
³常数过大
SleatorTarjan发明
2
 
2Link-Cut-Trees[3]
Link-Cut-Trees,简称LCT,它对上述操作的均摊时间不超过O(logn).
它所操作的对象是森树,能实现对于树的合并与分离.
a
b
c
d
e
f
g
h
i
FormerTree
这是一颗已经构建好的树(森林),我们用Link-Cut-Trees来维护它.
2.1Build
对于一个节点进行访问的操作称为Access;
称我们要表示的这棵树为FormerTree;
PreferredChild,如果对于节点u所在的子树中,节点v为最后访问过的
,则称节点v为节点uPreferredChild;
PreferredEdge,每个点到PreferredChild的边称为PreferredEdge.
preferredPath,preferredEdge构成的不可再延伸的路径称为Preferred
Path.
3
 
a
b
c
d
e
f
g
h
i
此时a为最后一次Access的点
假若上图就是对FormerTree标记了PreferredEdge的图.
AuxiliaryTree,在每一条PreferredPath,以路径上点的深度为关键字,
SplayTree来维护它,就是AuxiliaryTree;
AuxiliaryTree,每个点的左子树中的点,都在PreferrdPath中这个点的
上方;右子树中的点都在PrefreedPath中这个点的下方.
Pathparents,Pathparents来表示其AuxiliaryTree对应的PreferrdPath
最高的节点;
因为FormerTree可以用若干条PrefreedEdge来表示,用每个AuxiliaryTree
表示一条preferrdEdge,那么我们最终所构建的就是一颗用ParentsEdge
所有AuxiliaryTree连接起来的树.
因为AuxiliaryTree可以维护FormerTree的信息,因此在实际操作中,只需要
维护AuxiliaryTree.
4
 
对三条PreferrdEdge建立AuxiliaryTree
b
a
d
g
i
e
c
h
f
将所有的AuxiliaryTreePathParent连成一棵树
b
a
d
g
e
c
h
i
f
5
 
2.2Access
Access操作是Link-CutTrees的所有操作的基础.假设调用了Access(v),
那么从点v到根结点的路径就成为一条新的PreferredPath.如果路径上经
过的某个结点u并不是parent(u)PreferredChild,那么由于parent(u)
PreferredChild会变为u,原本包含parent(u)PreferredPath将不再包含结
parent(u)及其之上的部分.
在对节点v进行一次Access操作后,那么它的PreferredChild应当消失.
先将点v旋转到它所属的AuxiliaryTree的根,如果vv所属的Auxiliary
Tree中有右儿子(也就是v原来的PreferredChild),那么应该将vAuxiliary
Tree中的右子树(对应着vPreferredChild之下的PreferredPath)Auxil-
iaryTree中分离,并设置这个新的AuxiliaryTreePathParentv.
然后,如果点v所属的PreferredPath并不包含根结点,设它的PathParent
u,那么需要将u旋转到u所属的AuxiliaryTree的根,并用点v所属的
AuxiliaryTree替换到点u所属的AuxiliaryTree中点u的右子树,再将原来
u所属的AuxiliaryTree中点u的右子树的PathParent设置为u.
如此操作,直到到达包含根结点的PreferredPath.
这是一个递归操作的过程.最终目的还是完成对preferrdEdge的修改.
2.3Isroot
AuxiliaryTree,如果一个点是root,那么他的ParentNULL.
2.4Findroot
寻找点v所在AuxiliaryTree的根节点,先将v旋转到根,然后寻找其
AuxiliaryTree中最左边的点.
2.5Beroot
注意将节点v进行Access操作之后v只是变成了AuxiliaryTreeroot,
但在FormerTree中仍然不是root,因为v还有左子树.所以Beroot操作的
意义在于将节点v变成FormerTree中的root.
首先进行Access(v)Splay(v)操作,然后将vroot路径上点的深度反
,我们面对翻转的处理方法是先打标记然后在维护Splay时处理.
2.6Split
先访问v,然后把v旋转到AuxiliaryTree的根,然后再断开v在它的所属
AuxiliaryTree中与它的左子树的连接,并设置.
6
 
2.7Merge
先访问v,然后修改v所属的AuxiliaryTreePathParentw,然后再次
访问v.
2.8Code
1boolisroot(intx){
2returnc[fa[x]][0]!=x&&c[fa[x]][1]!=x;
3}
4
5voidberoot(intx){
6Access(x);
7Splay(x);
8rev[x]^=1;
9}
10
11voidpushdown(intk){
12intl=c[k][0],r=c[k][1];
13if(rev[k]){
14rev[k]^=1;rev[l]^=1;rev[r]^=1;
15swap(c[k][0],c[k][1]);
16}
17}
18
19voidrotate(intx){
20inty=fa[x],z=fa[y],l,r;
21if(c[y][0]==x)l=0;
22elsel=1;r=l^1;
23if(!isroot(y))
24if(c[z][0]==y)c[z][0]=x;
25elsec[z][1]=x;
26fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
27c[y][l]=c[x][r];c[x][r]=y;
28}
29
30voidsplay(intx){
31top=0;q[++top]=x;
32for(inti=x;!isroot(i);i=fa[i])
33q[++top]=fa[i];
7
 
34for(inti=top;i;i−−)pushdown(st[i]);
35while(!isroot(x)){
36inty=fa[x],z=fa[y];
37if(!isroot(y)){
38if(c[y][0]==x^c[z][0]==y)
39rotate(x);
40elserotate(y);
41}
42rotate(x);
43}
44}
45voidaccess(intx){
46for(intt=0;x;t=x,x=fa[x])
47splay(x),c[x][1]=t;
48}
49
50intfind(intx){
51access(x);splay(x);
52while(c[x][0])
53x=c[x][0];
54returnx;
55}
56voidmerge(intx,inty){
57beroot(x);access(y);splay(y);
58if(c[y][0]==x)
59c[y][0]=fa[x]=0;
60}
61voidsplit(intx,inty){
62beroot(x);fa[x]=y;
63}
3Pathoperation*[1]
针对的是对于路径的修改和查询.对于节点uv之间路径的操作,我们
首先Beroot(u),然后Access(v),Splay(v).然后发现u,v之间的路径在Auxiliary
Tree上都位于v的左子树.然后就进行各种维护就可以了.
8
 
3.1Example1
Bzoj2002弹飞绵羊
需要维护树的分离与合并.
如何建模?我们考虑用LCT,
我们注意到如果在u位置能到达 v位置那么可以连一条边(u,v),T
空点即到达此点时已经跳出,则查询从u几次跳出我们可以查询在Auxiliary
Treeu,t之间路径的长度即为答案.那么根据我们之前所提到的如何维护
路径,我们只需要Beroot(t),然后Access(u),Splay(u)答案即为size(t->le).
对于另一种操作即改变一个点所到达的点的位置,我们只需要切开原来
的边并且重新连一条边即可.
4Others
4.1Connect
对于树上路径信息的维护与修改,一般会用树链剖分来做.树链剖分是
将树划分成若干条路径并用线段树等数据结构来维护,我们可以看到树链
剖分与Link-Cut-Tree的联系与不同,都是将树转化成若干条链的方式,
于树链剖分,它只能维护静态的树的信息,却不能对树的形态进行改变;
Link-Cut-Tree能动态的改变树的形态,也能维护树的路径信息.例如,给出一
棵树,进行以下操作.
u子树和;
u子树加上一个数;
u,v之间路径和;
u,v之间每条边加上一个数;
像这种对树上路径进行查询修改的话我们可以用树链剖分做.
再例如,给出一棵树,进行以下操作.
改变u,v边权.
查询u,v路径最大值.
解法一 Lint-Cut-Tree
无疑问,这道题可以用LCT,这两种操作都是LCT所支持的基础操
,但是毫无疑问,因为这道题目没有涉及到更改树形态信息.LCT依据
AuxiliaryTree实现,不得不考虑Splay巨大的常数.
9
 
解法二 树链剖分
直接用线段树来维护路径最大值.
其他解法[3]
References
[1]PoPoQQQ.Link-cut-tree.
[2]DanielD.SleatorandRobertEndreTarjan.Adatastructurefordynamictrees.
pages114--122,1981.
[3]YangZhe.Someresearchonqtreesolution.
10
 
posted @ 2018-01-14 20:42  Grary  阅读(196)  评论(0编辑  收藏  举报
博客园 首页 私信博主 编辑 关注 管理 新世界