【总结】LCT算法的基础建模与应用

前言:

在恶心了几道LCT算法的题后,勉强领悟到一点LCT的用处
作为一个比较经典的数据结构,LCT的应用范围比较广泛,比如动态维护最小生成树,动态维护双联通分量,以及其它的一些动态路径询问的问题。尽管在子树问题上,LCT算法显得有些无力(出门左转找ET),但毕竟很多子树问题可以用DFN+Splay水过,但路径询问,却几乎找不到可以替代LCT的算法。

基本概念:

LCT算法,其实就本质而言,是一个十分暴力却优美的算法。优美在于,它基于一个神奇的树上算法:树链剖分。暴力在于,它的实现过程异常简单,只是用Access与Splay两个函数,就可以完成几乎所有操作。
树链剖分是LCT的理论基础,常规的树链剖分可以在 O(Nlog2N) O ( N l o g 2 N ) 复杂度内处理大多数路径、子树问题(但有时离线的点分治却能比它更优秀)。

树链剖分是预处理出来的,也就是说它不能支持对树的结构有修改的问题。而LCT就是用于解决这一漏洞的。

由于树的形态有变化,所以重边和轻边的关系要动态存储,在每一条重链上,都用一个Splay平衡树来维护这条链上的信息,而Access操作,是将该点与根的连线变为一条重链。具体的原理与时间复杂度的证明,这里不再赘述。

典型建模与应用:

首先,最常见也最简单的应用,是直接用懒标记来存储一些需要的信息。当然,有的这类题也可以把人恶心出病来,但绝大多数这类题目都属于不用太多思考就能过的。比如模板题Qtree系列,基本上都是这类题。

有点难度的,比如BZOJ3091城市旅行,
这道题需要4个懒标记互相配合修改:(以下有剧透成分)
由于题目要记录“路径任意选择两个点的期望”,所以很显然要统计所有方案总数,于是一个懒标记存储 n1a1+(n1)2a2+1nan n ∗ 1 ∗ a 1 + ( n − 1 ) ∗ 2 ∗ a 2 + … … 1 ∗ n ∗ a n
为了维护这个值,还要记录 1a1+2a2+3a3++nan 1 ∗ a 1 + 2 ∗ a 2 + 3 ∗ a 3 + … … + n ∗ a n ,以及 na1+(n1)a2++an n ∗ a 1 + ( n − 1 ) ∗ a 2 + … … + a n 具体做法与证明可以参考
PoPoQQQ大爷的博客

但稍微拓展一点,可以将LCT与其他一些比较常规的算法结合起来,以达到使那些经典算法“动”起来的目的。

最简单的:

LCT与最小生成树

考虑最小生成树的算法中,其实也有能够动态的算法:破圈算法,具体做法很简单,每次插入一个边后,如果其连接的两个点没有联通,则直接连上这条边,如果已经联通,则这两个点之间必有一条路径,将当前的边连上就会形成一个环,再删去这个环中最大的一条边即可。

LCT维护动态最小生成树,其实就是破圈算法的高效版,考虑破圈算法的劣势:由于其不能有效存储路径上的最大权边,所以导致每次删边都要将环上的边跑一次。对于LCT而言,这个限制是不存在的(如果不会动态维护路径最大值,请回上一部分)。只需要在懒标记中存储最大值,以及最大值所在的位置即可。

例题:BZOJ2594水管局长数据加强版
BZOJ3514Codechef MARCH14 GERALD07加强版,稍微有些变化,不过本质上还是需要LCT动态维护生成树。
BZOJ3669魔法森林相对于上一题而言,更考思维一些。

LCT与动态双联通分量(仅支持加边)

其实,看过LCT做动态最小生成树之后,很容易就能猜到这部分的方法。
同样的,我们维护的仍然只是一棵树,每次加入一条边,仍然和之前一样,未联通则联通,若已联通,则将这个环上所有点缩成一个点,这一个操作可以用LCT套并查集来实现,即在初始的变量中,加入一个新的参数表示其属于哪一个双联通分量内,每次Access操作时,必须更新其父亲(以使得不重复访问已被覆盖的点)。
缩点的操作,可以暴力来做(每个点只被缩一次)。

void change(node *x,node *y){//x即当前节点,y是代表当前强联通分量的点
    if(x!=y)
        x->xfa=y;
    if(x->ch[0]!=NIL)
        change(x->ch[0],y);
    if(x->ch[1]!=NIL)
        change(x->ch[1],y);
}

典型的题是BZOJ2959长跑

其他的LCT应用

说道这几天刷的LCT题,有一道实在恶心:BZOJ2759一个动态树好题
但也不得不承认,这的确是道好题,也拓宽了我对图论算法的认知(原来LCT也能解决数论问题)
这道题的最好的地方,在于它有效解决了LCT仅局限于懒标记修改的弊病。(以下有剧透成分)
在LCT上每个点表示一个变量,将p[i]设为i的父亲,整个图就形成了一个基环森林,将环的一条边断掉,断边的一个点作为根,另一个点作为特殊节点(称为 sfa s f a ),维护每个节点与该节点的线性关系(实现时,维护的是与该重链最左端的线性关系)。求解时,先用exgcd得到sfa的值,再将其带入之前存的线性关系中用exgcd求解。具体过程比较复杂,建议查本题的题解。

这一类应用可以说是比较恶心,也比较少见的一类题目。




总的来说,凡是做LCT的题目,不能直接先想用LCT怎么做,怎么打懒标记。而是应该从别的角度出发,得到一个需要LCT来维护的算法,再用LCT来优化。

你可能感兴趣的:(数据结构,最小生成树,动态树)