• 「学习笔记」LCT重学


    其实理论上算是第三次学了,寒假的时候就等于没学


    定义

    起源是有些树要动态加边或者删边,所以这时候用到了 (lct)

    我们把 (splay) 放到外层作为外层树,每个点被包含在一个单独的 (splay)

    其实这里主要是运用了 (splay) 可以区间反转的功能(当然可以 (fhq) 但是复杂度多一个 (log)

    同时在 (splay) 里面中序遍历得到的点的深度是递增的,也就是说不一定在当前 (splay) 的根就是这个子树里面深度最浅的点

    因此我们把边就得拆成实边和虚边,这里在同一个 (splay) 里面的边都是实边,而不在里面的边则是虚边

    这里注意:父子连边不变,不是说当前 (splay) 的根连向下个 (splay) 的根来表示连边

    那么就有一些定义

    (fa[x]:) (x)(splay) 里面的父亲,不是原树

    (ls[x],rs[x])(splay) 里面的左右儿子,真树里面的连边都是靠 (fa[son]=x) 来的

    操作

    access

    联通根到当前点的链

    这个每个 (splay) 跟着做就行了,每次把当前点干到 (splay) 的根,然后改儿子

    这里是把 (rs[fat]=now) ,根据深度的原则不难得到

    所以简单的代码如下:

    inline void access(int x){
    	for(reg int y=0;x;x=fa[y=x]) splay(x),rs[x]=y,push_up(x);
        return ;
    }
    

    其实这里是换了个更的方式:把更新儿子的步骤放到了上面,这里被覆盖的儿子的父亲没变,但是父子关系变成了虚边

    makeroot

    指定根

    很好说,直接打通链时候翻上去就行了,但是很坑的是这样的话没有深度保证

    所以要翻转整个当前 (splay),打标记即可,和普通 (splay) 没有区别

    inline void pushroot(int x){
    	swap(ls[x],rs[x]); fl[x]^=1; 
        return ;
    }
    inline void makeroot(int x){
    	access(x); splay(x); pushroot(x);
        return ;
    }
    

    findroot

    找到原树上的根

    换到根之就找左儿子,也就是:

    inline int findroot(int x){
        access(x); splay(x); 
        while(ls[x]) x=ls[x]; 
        return splay(x),x;//多splay来保证复杂度……
    }
    

    当然这样写是非常慢的,那么特定场景下可以用并查集来进行替换和卡常

    split

    打通一条链,随便钦定一个点为根然后连上就行了

    inline void split(int x,int y){
        makeroot(x); access(y); splay(y); 
        return ;
    }
    

    然后直接查询 (s[y]) 就是这个路径上面的信息,需要理解一下为什么这样就能维护出来单独的路径

    连接两个点之间的边

    inline void link(int x,int y){
    	make_root(x); if(findroot(y)!=x) fa[x]=y; 
        return ;
    }
    

    cut

    断开边,先把一个点转到必然是另一个的父亲然后判断合法性

    也就是说:

    inline void cut(int x,int y){
    	make_root(x); 
        if(findroot(y)!=x||fa[y]!=x||ls[y]) return ;
        //第一个是不在一个树上,findroot(y) 之后 x 是这个splay的根
        //如果y的父亲不是x那么必然没有连边,考虑findroot中的更改
        //如果有ls[y] 那么就有越级父亲
        rs[x]=0; fa[y]=0; push_up(x);
        return ;
    }
    

    这里写 (lct)(splay) 和一般的不太一样,具体如下:

    ((1)) 维护一个是不是当前splay的根的函数:

    inline bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
    

    ((2)) (rotate)(splay) 的时候记得判断 (z=fa[fa[x]]) 的情况,不能找到另一个splay上面

    ((3)) 下方 (makeroot) 标记的时候要注意从上往下,原因可以手玩一下

    这样的话板子就随便打了

    功能

    维护链上信息

    其实本质上就是 (split) 一下,然后有各种打标记的方式

    Luogu 1501

    裸题,直接打标记就行了

    Luogu 4332

    显然改变一个 ([n+1,3n]) 的点的本质会修改一条链上的答案

    肯定是临界的会改,问题转化成了维护链上最深的 (cnt[x]) 不是 (1/2) 的点的位置

    打通一条链的操作就是 (access) ,然后在平衡树上二分

    具体而言就是 (push\_up) 维护几个子树面是不是都是 (1/2) 即可

    时间复杂度 (O(nlog^2 n))

    貌似有一个少 (log) 的写法,就是记录子树里面的最深 (val eq 1/2) 的点

    如果修改值的话需要交换

    这题写的原则就是多 $push_up $

    维护双联通分量/联通性

    Luogu2542

    逆序之后考虑如何维护必经边

    这个必经边容易让人想到点双,那么考虑缩点

    每次如果 (link) 失败了就删掉环上的点,缩成一个新点,所有连的点都指向这个点

    但是需要更改的是 (access) 的时候是要跳 (find(fa[x]))

    维护生成树或者树边信息

    边权很难维护,如果按照一些树题放到儿子上的话一变父子关系就废掉了

    所以考虑拆点,把边权放到一个新的点上,因为比较优秀的编号方式,每个新点的 (id>n)

    所以更改或者一些其他操作就好说了

    link(e[i].id,e[i].x); link(e[i].id,e[i].y);
    cur(e[i].id,e[i].x); cut(e[i].id,e[i].y);
    

    NOI2014 魔法森林

    看到是多维的先想降维,所以 (sort)(a/b)

    那么然后的问题是如何维护一个用 (a) 最小的生成树

    那么每次加边如果成环就断掉环上最大的 (a) 然后更新答案

    为啥原来那么菜还要去抄题解呀

    维护虚子树信息

    (s_i) 表示虚子树信息,那么不一样的地方首先是 (access)

    for(reg int y=x;x;x=fa[y=x]){
        splay(x); 
        s[x]-=s[rs[x]],si[x]+=rs[x];
    	s[x]+=s[y],si[x]-=s[y];
        rs[x]=y;
    }
    

    然后 (link) 的时候要先把 (y) 整到根上面,防止祖先的信息被记漏

    inline void link(int x,int y){
        make_root(x); if(findroot(y)==x) return ;
        fa[x]=y; access(y); splay(y); si[y]+=s[x]; 
        push_up(y); //真的别忘记了多push_up
        return ;
    }
    

    BJOI2014 大融合

    貌似线段树合并随便做?

    如果 (lct) 的话考虑如果 (split(x,y)) 之后那么有剩下的边就是虚的了

    那么维护上 (s[x],si[x]) 查询的时候回答 (s[x] imes(s[y]-s[x]))

    维护树上染色联通块

    SP16549

    正在做

  • 相关阅读:
    四十一:HTML5之HTML5属性变化之input新增类型
    四十:HTML5之HTML5标签变化之删除的标签和重定义的标签
    三十九:HTML5之HTML5标签变化之新增的其他标签和简单的使用示例
    三十八:HTML5之HTML5标签变化之新增的web应用标签和简单的使用示例
    三十七:HTML5之HTML5标签变化之新增的多媒体标签和简单的使用示例
    三十六:HTML5之HTML5标签变化之新增的结构标签和简单的使用示例
    三十五:布局之布局小案例
    python初识-day2
    Python初识-day1
    leetcode maximum product subarray
  • 原文地址:https://www.cnblogs.com/yspm/p/14194139.html
Copyright © 2020-2023  润新知