背模板吧
LCT
LCT,Link Cut Tree,中文叫做动态树,顾名思义,是一种维护动态的树的数据结构,可以维护一个森林,支持加边、删边操作(但必须保证是树),时间都是 $O(\log n)$ ,常数有点大
思路
其实主要思路是将一棵树拆成很多个splay
虚实边
和树剖类似,动态树将边分为虚边和实边,每一个点到其儿子节点的所有边中,最多有一条实边(可以没有),而于树剖不同的是,我们用平衡树维护实边构成的路径,具体地,每一条只由实边构成的、极大的路径(特别的,如果一个点没有实边与其相连,我们认为这个点对应一个只包含自己的实边路径)都对应一个splay,splay的中序遍历,就是要维护的路径(从上到下),而splay的后继和前驱就对应树上的父子关系,而对于虚边,我们用splay的根节点维护(具体的,根节点的父亲对于虚边上的父亲), 如图,实线对于实边,虚线对于虚边,红色部分对于一棵splay,不难发现,在这样的定义下,所有点都对应位于的实边路径:

为了维护虚实边,我们有以下操作
accecss
$access(x)$ 可以将树上从根节点到点 $x$ 之间的所有边变成实边(明显它们也就在同一个实边路径上了),并且 $x$ 下方再无该实边路径上的点(换句话说, $x$ 就是该实边路径的终点),这是LCT最基本的操作,执行完毕后 $x$ 就是所在splay的根,具体如图:

如果要把 $x$ 和 $y$ 所在实边相连,可以把 $y$ 转到它所在splay的根,此时 $y$ 的左子树就对应 $y$ 所在实边中 $y$ 上方的部分,右子树就对应 $y$ 所在实边中 $y$ 下方的部分,然后直接让 $y$ 的右儿子指向 $x$ (我们从下往上进行,所以这时的 $x$ 是处理完毕的,并且一定是其所在实边对应slpay的根,这相当于将 $x$ 所在的splay拿来代替 $y$ 的右子树),这样, $y$ 所在实边中 $y$ 下方的部分就变成 $x$ 所在的那条实边了,特别的,第一次操作时让 $x$ 的右子树为空(保证 $x$ 就是该实边路径的终点)
要完成access,只需从 $x$ 开始向上操作,直到根节点即可
is root
$is\_rt(x)$ 可以判断 $x$ 是否是所在slpay的根节点,注意判断的不是原树的根节点,只需看 $x$ 的父亲是否有 $x$ 这个儿子即可(slpay根节点的父亲维护的时虚边)
make root
$make\_rt(x)$ 可以将 $x$ 变成所在树的根节点(因为树是无根树),只需先 $access(x)$ ,此时 $x$ 和根必定是同一实边路径的终点和起点,翻转即可
find root
$find\_rt(x)$ 可以找到 $x$ 所在树的根节点,并将其转到splay的根节点上,只需先 $access(x)$ ,然后一直向 $x$ 的左子树走即可,走时注意push down
split
$split(x, y)$ 可以将 $x$ 到 $y$ 的路径变成实边路径,只要先 $make\_rt(x)$ ,然后 $access(y)$ 即可
link
$link(x, y)$ 判断 $x, y$ 是否联通,如果不连通就加边 $(x, y)$ ,只要 $make\_rt(x)$ 然后判断 $find\_rt(y)$ 是否等于 $x$ 不等则令 $fa(x) = y$ (因为make root后 $x$ 一定是splay的根节点)
cut
$cut(x, y)$ 判断 $x, y$ 之间是否有边,如果有,就删除边 $(x, y)$ ,只要 $make\_rt(x)$ 然后判断 $find\_rt(y) = x \wedge fa(y) = x \wedge y\text{没有左儿子}$ ,成立就断开 $x, y$ 之间的边(双向断),然后push up
代码
1 |
|