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
4Others 9
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 ³
这几种数据结构都存在一定的局限性 ,因此动态树问题并没有被完全解
.
在信息学奥赛中 ,我们常常会遇到动态树的简化问题 .我们涉及的操作只
有对树形态的操作和对于路径的操作 .因此就有一种解决动态树问题的数据
结构
Link cut Trees
¹ 不支持路径操作
² 不支持树权操作
³ 常数过大
Sleator Tarjan 发明
2
 
2Link-Cut-Trees[ 3]
Link-Cut-Trees, 简称LCT, 它对上述操作的均摊时间不超过O(log n).
它所操作的对象是森树 ,能实现对于树的合并与分离 .
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), 那么应该将v Auxiliary
Tree 中的右子树( 对应着v PreferredChild 之下的PreferredPath) Auxil-
iaryTree 中分离, 并设置这个新的AuxiliaryTree PathParent v.
然后 ,如果点 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
1 boolisroot(intx){
2 returnc[fa[x]][0]!=x&&c[fa[x]][1]!=x;
3 }
4
5 voidberoot(intx){
6 Access(x);
7 Splay(x);
8 rev[x]^=1;
9 }
10
11 voidpushdown(intk){
12 intl=c[k][0],r=c[k][1];
13 if(rev[k]){
14 rev[k]^=1;rev[l]^=1;rev[r]^=1;
15 swap(c[k][0],c[k][1]);
16 }
17 }
18
19 voidrotate(intx){
20 inty=fa[x],z=fa[y],l,r;
21 if(c[y][0]==x)l=0;
22 elsel=1;r=l^1;
23 if(!isroot(y))
24 if(c[z][0]==y)c[z][0]=x;
25 elsec[z][1]=x;
26 fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
27 c[y][l]=c[x][r];c[x][r]=y;
28 }
29
30 voidsplay(intx){
31 top=0;q[++top]=x;
32 for(inti=x;!isroot(i);i=fa[i])
33 q[++top]=fa[i];
7
 
34 for(inti=top;i;i−−)pushdown(st[i]);
35 while(!isroot(x)){
36 inty=fa[x],z=fa[y];
37 if(!isroot(y)){
38 if(c[y][0]==x^c[z][0]==y)
39 rotate(x);
40 elserotate(y);
41 }
42 rotate(x);
43 }
44 }
45 voidaccess(intx){
46 for(intt=0;x;t=x,x=fa[x])
47 splay(x),c[x][1]=t;
48 }
49
50 intfind(intx){
51 access(x);splay(x);
52 while(c[x][0])
53 x=c[x][0];
54 returnx;
55 }
56 voidmerge(intx,inty){
57 beroot(x);access(y);splay(y);
58 if(c[y][0]==x)
59 c[y][0]=fa[x]=0;
60 }
61 voidsplit(intx,inty){
62 beroot(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
Tree u,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
 

转载于:https://www.cnblogs.com/qdscwyy/p/8284280.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值