虽说是学习笔记但却是复习笔记,这篇文章将会慢慢(因为我正在学)带大家学会LCT的各种用途,以下是我的复习/学习历程。
Step.0 熟悉模板
这是最重要的内容,(以后忘记了还要再来一遍)给大家几道题敲一敲,如果觉得LCT还背不熟一定要敲,不要嫌烦。
Luogu1501 SP9577
Step.1 LCT维护生成树
先来看一道题
考虑使用Kruskal的方法,将边按照边权从小到大加入生成树,如果联通则构成了一棵生成树。但是还要要求差值最小,也就是最小值最大,所以每次形成环的时候要在环上找到一个边权最小的边,然后把它断掉。
我们知道LCT可以非常方便地维护链上的信息,那么我们维护一条链的边权最小的边的编号就可以了。
如果已经做好Step.0的话相信代码不难写出。
然后就可以再看一道题了。
Step.2 LCT维护子树信息
我们知道LCT可以非常方便地维护链信息,却不能方便地维护子树信息,因为LCT与树链剖分一样,将整棵树剖成了一条一条的链(每条链是一棵Splay)。
于是我们注意到维护链信息其实就是把虚边忽略之后的子树,那么我们可以通过额外考虑虚子树的贡献来维护子树信息。
看一道题
我们需要维护的就是子树大小,因此需要记录一个$siz2[]$来额外记录虚子树的大小。
所以pushup的时候要改为
1 inline void pushup(int x){ 2 siz[x] = 1 + siz2[x]; 3 if(ch[x][0]) siz[x] += siz[ch[x][0]]; 4 if(ch[x][1]) siz[x] += siz[ch[x][1]]; 5 }
这样$siz[]$维护的就是整棵子树的大小,然后我们只用考虑哪些操作对虚子树大小产生了影响。
rotate和splay操作:没有改变边的虚实情况,所以没有影响
access操作:每次splay $x$到根后要改变$x$的右儿子,所以$siz2[x]$加上原来右儿子的大小减去新的右儿子的大小。
makeroot和findroot操作:没有直接改变边的虚实情况,所以不需考虑。
cut操作:改变了子树大小,但每次cut之后需要pushup,所以不需考虑。
link操作:将$x$向$y$连了一条虚边,因此需要将$siz2[y]$要加上$siz[x]$。
其他跟普通LCT没有任何区别,那也不贴代码了。
Step.# 离线维护图联通性
这是学习LCT的时候无意看见的,详见CF1140F
Step.3 LCT维护****
因为我鸽了,所以下次再见。