推荐这篇论文
前置技能
简介
动态树问题, 简称LCT,近几年在OI中兴起的一种新型问题,是一类要求维护一个有根树森林,支持对树的分割, 合并等操作的问题。
算法用途
比如维护一个数据结构,有如下操作:
1.查询路径权值和。
2.查询LCA。
3.修改单个点的权值。
4.断开两个点,使之成为两棵树。
5.合并两个点,使之成为一颗树。
当然还可以有关于子树的操作,但是这个需要用到另一种数据结构叫TopTree。这里暂时只讲LCT。
算法实现
几个定义
Preferred Child:在v的子树中,如果最后被访问的节点在子树w中,则称w是v的Preferred Child。特殊的,如果最后被访问的节点就是v本身,则v没有Preferred Child。
Preferred Edge:连接v与其Preferred Child的边。
Preferred Path:由Preferred Edge构成的不可再延伸的路径。
Path Parent:Preferred Path中最高点的父亲节点。特殊的,如果这条Preferred Path的最高点就是根节点,则这条Path没有Path Parent。
因为操作需要支持分离与合并,因此我们用Splay来维护每一条Preferred Path。
各种操作
Access(x)
所谓access(x),即访问x节点。当x被访问后,x到根节点的路径就变成了Preferred Path。
给张图体会一下:
Access的实现非常暴力。假设当前节点为x,上一个节点为v(即x是v所在Preferred Path 的 Path Parent)。每次直接把x的Preferred Child。但是由于有Splay,我们就先把x Splay到根。如果x有右子树(即x的Preferred Child),那么就把x与它的右子树断开,接上v。x原来的右子树就重新建一颗树,其Path Parent为它自己。一直做到Preferred Path包含根节点为止。
MakeRoot(x)
让x成为树的根。
先Access(x),此时x与根节点就在同一颗平衡树上了。接着把x Splay到根,因为x必然是最深的点,所以Splay后所有的节点都在x的左边。此时只要交换x的左右子树,就可以把x变成树的根了。而这个操作可以同区间翻转一样打个Tag来实现。
Cut(x,y)
断开x和y。
先MakeRoot(x),再Access(y),此时x和y就在同一颗平衡树上了,且这棵树上只有x和y,根节点为y。再把y的左子树(x)和x的父亲(y)清零。