• LCT摘要


    介绍、用途

    LCT是树链剖分中的一种,又叫实链剖分、动态树,常用于维护动态的树、森林。

    维护方式

    LCT并不直接维护原树,而是用一堆splay作为辅助树来维护。原树中的一条实链上的点在一棵splay中,虚边体现为辅助上的连接两棵splay的虚边,只认爸爸不认儿子。

    变量介绍

     1 int n,m;
     2 struct Node {
     3     int fa,son[2];                                                                                            //爸爸、儿子(0左1右)
     4     int val,all;                                                                                            //该点权值、子树异或和
     5     char ifz;                                                                                                //是否翻转(0否1是)
     6     void res() {                                                                                            //重置(然并卵)
     7         fa=son[0]=son[1]=val=0;
     8     }
     9 } tree[maxn];
    10 int pre[maxn],inp;                                                                                            //翻转序列(splay用)
    var

    各种操作

    判断一个点是哪个儿子

    不多说了

    1 char which(int x) {
    2     return x==tree[tree[x].fa].son[1];
    3 }
    which

    判断一个点是不是该splay的根

    也不多说了

    1 char isroot(int x) {
    2     return x!=tree[tree[x].fa].son[which(x)];
    3 }
    isroot

    splay的操作

     1 void rotate(int x) {
     2     int f=tree[x].fa,ff=tree[f].fa,c=which(x);
     3     if(!isroot(f)) tree[ff].son[which(f)]=x;                                                                //若它爸是根就不要搞它爷了
     4     tree[x].fa=ff;
     5     tree[f].son[c]=tree[x].son[c^1];
     6     tree[tree[f].son[c]].fa=f;
     7     tree[x].son[c^1]=f;
     8     tree[f].fa=x;
     9     update(f);
    10     update(x);
    11 }
    12 void splay(int x) {
    13     int f;
    14     pre[inp=1]=x;
    15     for(f=x; !isroot(f); f=tree[f].fa) pre[++inp]=tree[f].fa;                                                //挖出它到根的点
    16     fdi(i,inp,1,1) pushdown(pre[i]);                                                                        //全部pushdown
    17     for(; !isroot(x); rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);    //无需pushdown
    18 }
    splay

    打通这个点到原树的根为实链

    这个是重点!!!是LCT的核心!!!

    首先,先将该节点splay到根,并将其爸爸splay到根。于是我们知道,它爸爸的右儿子深度大于它爸爸,是需要砍成虚边的点,而它的深度也大于它爸爸,所以直接将它爸爸的右儿子变成它。重复上述操作,直到它无爸爸。

    1 void access(int x) {
    2     for(int pr=0; x; pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr,update(x);
    3 }
    access

    将这个点变成原树的根

    先打通这个点到根,并将它splay到根。然后我们可以发现,不在这棵splay上的点不受影响,而这棵splay上的点深浅颠倒,对应到splay上就是区间翻转。所以给它打上一个翻转标记。

    1 void makeroot(int x) {
    2     access(x);
    3     splay(x);
    4     tree[x].ifz^=1;                                                                                            //打翻转标记
    5 }
    makeroot

    查找这个点所在原树的根

    先打通它到根并splay,然后找到它所在splay的最左边的点(即一直往左儿子找)。

    1 int find(int x) {
    2     for(access(x),splay(x); tree[x].son[0]; x=tree[x].son[0]);
    3     return x;
    4 }
    find

    连接个点并连接两棵树

    将一个点变成根,并令这个点爸爸为另一个点。注意先判断这两个点在不在一棵树内,在就不用连了。

    1 void link(int x,int y) {
    2     makeroot(x);
    3     tree[x].fa=y;
    4 }
    link

    切断两点之间的边

    先判断在不在一棵树内,不在就不切。然后将一个点变成根,另一个点打通到根并splay到根。易发现若这两个点间有边则这棵splay中只有它们俩。判断一下即可。

    1 void cut(int x,int y) {
    2     makeroot(x);
    3     access(y);
    4     splay(y);
    5     if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1])tree[y].son[0]=tree[x].fa=0;
    6 }
    cut

    改变一个点的值

    将这个点变成根,并将其splay,再改变权值即可。

    1 void change(int x,int y) {
    2     makeroot(x);
    3     splay(x);
    4     tree[x].val=y;
    5     update(x);
    6 }
    change

    查询x到y的异或和

    将x变成根,打通y并splay,直接查询即可。

    1 int query(int x,int y) {
    2     makeroot(x);
    3     access(y);
    4     splay(y);
    5     return tree[y].all;
    6 }
    query

    时空复杂度

    时间复杂度

    splay:均摊O(logn)的不用说了吧

    access:由于每次access最多有logn条实边变成虚边,splay复杂度也仅为均摊O(logn),因此时间复杂度均摊O(logn)

    makeroot:makeroot的开销主要为access,因此也为均摊O(logn)

    其他:基于以上三种操作,因此都为均摊O(logn)

    只是常数无比巨大!!!
    只是常数无比巨大!!!
    只是常数无比巨大!!!

    空间复杂度

    显然是O(n)的

    题目

    洛谷P3690 【模板】Link Cut Tree (动态树)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ImaxnF 0x7fffffff
     4 #define ME 0x7f
     5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
     6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
     7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
     8 #define fel(i,a) for(register int i=h[a];i;i=ne[i])
     9 #define ll long long
    10 #define MEM(a,b) memset(a,b,sizeof(a))
    11 #define maxn (300000+10)
    12 int n,m;
    13 struct Node{
    14     int fa,son[2];
    15     int val,all;//,siz;
    16     char ifz;
    17     void res(){fa=son[0]=son[1]=val=/*siz=*/0;}
    18 }tree[maxn];
    19 int pre[maxn],inp;
    20 template<class T>
    21 inline T read(T &n){
    22     n=0;int t=1;char ch;
    23     for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-1:n=ch-'0';
    24     for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-'0';
    25     return (n*=t);
    26 }
    27 template<class T>
    28 T write(T n){
    29     if(n<0) putchar('-'),n=-n;
    30     if(n>=10) write(n/10);putchar(n%10+'0');
    31 }
    32 template<class T>
    33 T writeln(T n){
    34     write(n);putchar('
    ');
    35 }
    36 char which(int x){return x==tree[tree[x].fa].son[1];}
    37 char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];}
    38 void update(int x){tree[x].all=tree[tree[x].son[0]].all^tree[tree[x].son[1]].all^tree[x].val;}
    39 void pushdown(int x){
    40     if(tree[x].ifz){
    41         tree[x].ifz=0,swap(tree[x].son[0],tree[x].son[1]);
    42         tree[tree[x].son[0]].ifz^=1,tree[tree[x].son[1]].ifz^=1;
    43     }
    44 }void rotate(int x){
    45     int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x;
    46     tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^1];tree[tree[f].son[c]].fa=f;
    47     tree[x].son[c^1]=f;tree[f].fa=x;update(f);update(x);
    48 }void splay(int x){
    49     int f;pre[inp=1]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,1,1) pushdown(pre[i]);
    50     for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);//update(x);
    51 }void access(int x){for(int pr=0;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr,update(x);}
    52 void makeroot(int x){access(x);splay(x);tree[x].ifz^=1;}
    53 int find(int x){for(access(x),splay(x);tree[x].son[0];x=tree[x].son[0]);return x;}
    54 void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1]/*tree[y].siz==2*/)tree[y].son[0]=tree[x].fa=0;}
    55 void link(int x,int y){makeroot(x);tree[x].fa=y;}
    56 void change(int x,int y){makeroot(x);splay(x);tree[x].val=y;update(x);}
    57 int query(int x,int y){makeroot(x);access(y);splay(y);return tree[y].all;}
    58 int main(){
    59     read(n);read(m);
    60     fui(i,1,n,1) tree[i].val=read(tree[i].all);
    61     fui(i,1,m,1){
    62         int opt,x,y;
    63         read(opt);read(x);read(y);
    64         switch(opt){
    65             case 0:writeln(query(x,y));break;
    66             case 1:if(find(x)!=find(y)) link(x,y);break;
    67             case 2:if(find(x)==find(y)) cut(x,y);break;
    68             case 3:change(x,y);
    69         }
    70     }
    71     return 0;
    72 }
    AC代码

    BZOJ2049 [Sdoi2008]Cave 洞穴勘测

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ImaxnF 0x7fffffff
     4 #define ME 0x7f
     5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
     6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
     7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
     8 #define fel(i,a) for(register int i=h[a];i;i=ne[i])
     9 #define ll long long
    10 #define MEM(a,b) memset(a,b,sizeof(a))
    11 #define maxn (10000+10)
    12 int n,m;
    13 struct Node{
    14     int fa,son[2];
    15     char ifz;
    16 }tree[maxn];
    17 int pre[maxn],inp;
    18 template<class T>
    19 inline T read(T &n){
    20     n=0;int t=1;double x=10;char ch;
    21     for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-1:n=ch-'0';
    22     for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-'0';
    23     if(ch=='.') for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-'0')/x,x*=10;
    24     return (n*=t);
    25 }
    26 char which(int x){return x==tree[tree[x].fa].son[1];}
    27 char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];}
    28 void pushdown(int x){
    29     if(tree[x].ifz){
    30         tree[x].ifz=0,swap(tree[x].son[0],tree[x].son[1]);
    31         tree[tree[x].son[0]].ifz^=1,tree[tree[x].son[1]].ifz^=1;
    32     }
    33 }
    34 void rotate(int x){
    35     int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x;
    36     tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^1];tree[tree[f].son[c]].fa=f;
    37     tree[x].son[c^1]=f;tree[f].fa=x;
    38 }
    39 void splay(int x){
    40     int f;pre[inp=1]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,1,1) pushdown(pre[i]);
    41     for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);
    42 }
    43 void access(int x){for(int pr=0;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr;}
    44 void makeroot(int x){access(x);splay(x);tree[x].ifz^=1;}
    45 int find(int x){for(access(x),splay(x);tree[x].son[0];x=tree[x].son[0]);return x;}
    46 void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1])tree[y].son[0]=tree[x].fa=0;}
    47 void link(int x,int y){makeroot(x);tree[x].fa=y;}
    48 int main(){
    49     read(n);read(m);
    50     fui(i,1,m,1){
    51         int x,y;char opt;
    52         for(opt=getchar();opt!='Q'&&opt!='C'&&opt!='D';opt=getchar());read(x);read(y);
    53         switch(opt){
    54             case 'Q':puts((find(x)==find(y))?"Yes":"No");break;
    55             case 'C':if(find(x)!=find(y)) link(x,y);break;
    56             case 'D':cut(x,y);break;
    57         }
    58     }
    59     return 0;
    60 }
    AC代码

     

    作者:A星际穿越
    我的博客写得这么烂,应该不会有人想转载的吧
    如果要转载的话,请在文章显眼处标明作者和出处谢谢
  • 相关阅读:
    Make a web page as screensaver
    python写入sqlserver中文乱码问题
    单片机中的ROM,RAM和FLASH的作用
    单片机STM32F4系统内部中断和外部中断优先级问题
    单片机AHB和APB
    嵌入式编程中一些常用的转换函数
    'AVStream::codec': 被声明为已否
    Python安装Python_snappy安装失败
    .net core 数据库问题
    .net 5.0 中 CrystalQuartz 增加授权校验
  • 原文地址:https://www.cnblogs.com/Axjcy/p/9520253.html
Copyright © 2020-2023  润新知