【参考】:我觉得写得最清楚的一个:https://oi.men.ci/link-cut-tree-notes/
https://www.cnblogs.com/flashhu/p/8324551.html
论文什么的太专业了我一半没耐心去看:https://wenku.baidu.com/view/75906f160b4e767f5acfcedb
【背景】:反正就是拖了很久,然后最近稍微练习了一下splay和树剖之后才有勇气去看,因为三个月前看晕了,然后就一搁着了。
但是最近遇到了LCT解决可以删边的并查集问题,是时候补一波了。
主要会晕的原因就是有两棵树:splay树和原树,如果博主没写清楚,或者理解不到位就可能搞晕。
现在大概看懂后理解是这样的:原树是虚拟的,用来满足我们的常识。 而真正的操作基本上都是在splay树上的,只是splay树上的操作要满足原树也成立。
所以加边、删边操作需要把节点移到splay树的根节点;同时,由于splay到根,为了满足原树的形状,需要翻转操作。
注意1:如何理解这里的一颗splay树? :一颗splay树是一个原树上的链。
。这个splay的根没什么特殊的,它不一定是原树的根,把它splay到根节点只是为了“加边”和“删边”等操作。
。这个splay的排序是根据深度的,所以“翻转”操作只需要交换左右儿子即可。
。原树是多叉树,splay是二叉树。有人会疑惑为什么二者能互相表示呢? 其实原树并没有记录儿子关系,只记录了父亲关系(splay树的父亲关系转化可得),但我们找X到根root的路径是没问题的(一直上找)。 而splay树则记录的父亲和儿子关系,rotate,pushdown等操作不成问题。
。make_root(move_to_root)是针对原树,splay是针对splay树,但是操作都是在splay树上实现。
(只要一直注意到这几点应该就不太会搞晕了)
注意2:由于有多棵splay树,所以不能记录root节点,而需要用isroot函数判定。
注意3:关于合并,一棵splay树上有一个par_path,它在根节点位置A,代表通过根节点连接到另外一棵树X,设B为X的根节点,连接后之前的根节点A就不是根节点了,所以A处没有par_path,新树的par_path在被连接的树X的根节点B处。
注意4:关于查找两个节点X、Y是否在同一棵树上:因为splay的根节点是不固定的,也没有代表性,所以需要找“原树”的根节点。 具体的:把Xaceess,然后splay到根,然后就可以向左边找深度第的节点,直到找到原树的根节点。
注意5:统计u到v的链上的信息,比如u-v链的节点权值和。和splay求区间和以及树剖的方法不同:直接在把u移到根(原树,则u深度最小),然后aceess-v(aplsy树,则v深度最大。)然后把v节点spaly到根(形如一条只有左儿子的链),就可以直接统计了,sum[X]=val[X]+sum[left_son]。
其他的,凭借自己的想象力,就可以大概打出伪代码了。
accessX:常规,使X到所在的根的链成为重链,从下到上。
splayX:稍微差别,需要先pushdown。
rotateX:常规。
updateX:常规。
...‘
所以只要有splay的基础,还是不难“入门”LCT的。