【笔记】Link-Cut-Tree

来源:互联网 发布:牛仔精神是什么知乎 编辑:程序博客网 时间:2024/04/27 15:55

动态树问题。

维护一个森林,支持树上动态查询、修改、删边、加边、换根等等,但始终保持是一颗树。

我学的主要是路径查询和修改。貌似路径和子树不能兼顾…但有一个很厉害的数据结构叫Top Tree,能同时兼顾,听起来好厉害,资料也不多,并没有学…

好了,来说一下正题。

先想一下树链剖分,引入了轻重链剖分的思想,可以高效地做树上查询&&修改操作,但不能加边删边,也就是相当于一个“静态树”。

而动态树,也引入了把树剖成链的思想,但轻重链和树剖中定义不同。

定义操作access(u),表示从u为起点,向当前树的根节点连一条重链,这条重链的端点分别为当前树的根节点和u。

对于每一条重链,我们用一棵Splay维护,关键字为重链上点的深度。

(这里插一句,如何做到“动态树”?可以把上面的操作做一个等价转换,映射到某些数据结构中(例如动态树问题就是把它转到splay里做),这样可能可以得到相同的效果。)

然后,对于操作access(u),可以高效地利用splay做到。

access操作:

这里写图片描述

图片来自论文【QTREE解法的一些研究】

相当于把沿途的重链所组成的splay断开,接上N节点所拉的重链。

介绍完access操作,先来看一下换根(toroot(u))操作。

若想让u成为当前树的根节点,则可以先access(u),再splay(u),把u转为当前splay的根节点。因为splay维护的是深度,所以u没有右儿子(没有比u还要深的点,因为重链定义),所以换根就相当于一次区间翻转,交换左右子树即完成区间翻转。此时就可以打标记了。

这样,toroot(u)操作的过程,就是先access(u),再splay(u),再翻转。

提取u到v的路径,可以先toroot(u),再access(v),此时v到根节点u所拉的重链即为所求路径,也就是整棵splay。

说了这么多重链,轻边呢?对于轻边我们需要单独记录处理。在原树上,当前重链的顶端节点与他的父亲的边即为轻边,如果不记录,树将是不完整的。

具体到代码实现,可以是当前splay的根节点的父亲即为当前splay上面的那个重链所在的splay上的点,但上面的splay的儿子并不指向当前splay的父亲,即为用splay的根节点的父亲来存储轻边。

像下图一样:
这里写图片描述

其中没画的指针皆指向null。

这样就把所有的splay串了起来,但因为Splay根节点的父亲指针不是null,所以有诸多细节需要注意。

这样,在u和v之间连边,可以toroot(u),再让u的父亲为v,这就相当于v本身是一棵splay,而u和v之间连了条轻边。

断开u和v之间的边,可以先toroot(u),再access(v),再splay(v),此时v的左儿子必定为u,于是断开v和v的左儿子即可。

路径操作可以用splay的下放标记来实现,和splay的基本操作一样。

但标记下放有一个问题。因为splay是时时刻刻在分裂与合并的,因为要动态维护每条重链,所以在splay之前,要先把根节点到当前节点全部下放一遍标记,防止标记下放不完全。


关于LCT,我就学了这些,对于代码上的细节还有算法的细节纠结了好久,也是好不容易搞懂了…欢迎神犇们打脸+补充……

参考资料:论文【QTREE解法的一些研究】
网上各种LCT题目的代码

特别感谢:

善良的学长小绿
以及不知道抄的谁的代码2333

0 0
原创粉丝点击