zoukankan      html  css  js  c++  java
  • LCT动态树入门

    LCT,link-cut-tree,一种基于splay的高级数据结构,常用于维护动态森林问题,但ta只能维护子树信息,无法修改子树信息。
    首先,如果你不会splay,来这里看看吧。
    接下来步入正题。
    首先阐述一下个人对LCT的理解,其实你可以把LCT理解成许多棵splay,每一个联通块是一棵大splay,每个大splay中有许多的小splay,小splay的根靠虚边连上大splay,虚边是什么?即假设你从x向y连一条虚边,便只要将x父亲设为y,不用将y的儿子设为x。
    但每一棵小splay中的边都是正常的树边。那么,判断一个点是不是splay的根,就要这样

    inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    

    那么,每棵小splay中的到底什么?你可以将其理解为你查询的历史路径。因为每当你要统计一条路径上的信息,就要用到access操作

    inline void access(int x)
    {
        for(register int y=0;x;y=x,x=fa[x])
        {
            splay(x);ch[x][1]=y;push_up(x);
        }
    }
    

    access看英文就知道,这个函数相当于打通了一条从该节点到root的通道,每次将这个点旋到当前splay的root,再将以前那一棵的root接到这一棵的右儿子上,就这样一路旋上去,最后,这个点会停在主splay的最下面。

    inline void makeroot(int x)
    {
        access(x);splay(x);rev[x]^=1;
    }
    

    makeroot,顾名思义,把这个节点作为根,为什么要打翻转标记?因为LCT维护每个点的相对深度,当你把一个点旋到root,例如是一条链,你把点从最右边旋上来,那么本来这个点应该是最深的,现在变成最浅的了,所以打上翻转标记,使他依然是最深的。
    当你要统计x到y路径上的某些信息是,只需要makeroot(x),access(y),这时,x到y中间的点就是你要的路径,再splay(y),y旋上去时push_up就会把路径上的信息统计掉,所以ans就是y点信息了。

    int l[N];
    inline void splay(int x)
    {
        l[0]=0;
        int y=x;
        while(1)
        {
            l[++l[0]]=y;
            if(isroot(y))break;
            y=fa[y];
        }
        Fordown(i,l[0],1)push_down(l[i]);
        while(!isroot(x))
        {
            //if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
            rotate(x);
        }
    }
    

    这里的splay,因为有翻转操作,所以要先把路径上所有点入队,从上面开始依次下放翻转标记

    inline int find(int x)
    {
        access(x);splay(x);
        while(ch[x][0])x=ch[x][0];
        return x;
    }
    

    find操作,其实就是找到左边的点,用来判断两个点在不在一个联通块中

    inline void link(int x,int y)
    {
        if(find(x)==find(y))return;
        makeroot(x);
        fa[x]=y;
    }
    inline void cut(int x,int y)
    {
        makeroot(x);
        access(y);splay(y);
        if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
    }
    

    如果你弄清楚了前面几个操作,那么link和cut操作就很简单了

    最后,在rotate的时候,如果你需要维护路径上的信息就需要push_up,但因为题目的不同而push_up的东西不一样,所以要根据题目来定,这里就拿维护路径上的^值,所以这样写

    inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
    

    下面是例题
    洛谷LCT模板(3690)要就维护路径^值

    #include<bits/stdc++.h>
    using namespace std;
    typedef int sign;
    typedef long long ll;
    #define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
    #define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
    const int N=3e5+5;
    bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
    bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
    template<typename T>T read()
    {
        T ans=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
        return ans*f;
    }
    void file()
    {
        #ifndef ONLINE_JUDGE
            freopen("LCT.in","r",stdin);
            freopen("LCT.out","w",stdout);
        #endif
    }
    int n,m,val[N],sum[N];
    int ch[N][2],fa[N],rev[N];
    inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
    inline void push_down(int x)
    {
        if(rev[x])
        {
            rev[ch[x][0]]^=1;
            rev[ch[x][1]]^=1;
            swap(ch[x][0],ch[x][1]);
            rev[x]=0;
        }
    }
    inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    inline bool get(int x){return x==ch[fa[x]][1];}
    inline void rotate(int x)
    {
        int old=fa[x],oldfa=fa[old],o=get(x);
        if(!isroot(old))ch[oldfa][get(old)]=x;
        fa[x]=oldfa;fa[ch[x][o^1]]=old;fa[old]=x;
        ch[old][o]=ch[x][o^1];ch[x][o^1]=old;
        push_up(old);push_up(x);
    }
    int l[N];
    inline void splay(int x)
    {
        l[0]=0;
        int y=x;
        while(1)
        {
            l[++l[0]]=y;
            if(isroot(y))break;
            y=fa[y];
        }
        Fordown(i,l[0],1)push_down(l[i]);
        while(!isroot(x))
        {
            if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
            rotate(x);
        }
    }
    inline void access(int x)
    {
        for(register int y=0;x;y=x,x=fa[x])
        {
            splay(x);ch[x][1]=y;push_up(x);
        }
    }
    inline void makeroot(int x)
    {
        access(x);splay(x);rev[x]^=1;
    }
    inline int find(int x)
    {
        access(x);splay(x);
        while(ch[x][0])x=ch[x][0];
        return x;
    }
    inline void link(int x,int y)
    {
        if(find(x)==find(y))return;
        makeroot(x);
        fa[x]=y;
    }
    inline void cut(int x,int y)
    {
        makeroot(x);
        access(y);splay(y);
        if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
    }
    void work()
    {
        while(m--)
        {
            static int opt,x,y;
            opt=read<int>();x=read<int>();y=read<int>();
            if(opt==0)
            {
                makeroot(x);
                access(y);splay(y);
                printf("%d
    ",sum[y]);
            }
            else if(opt==1)link(x,y);
            else if(opt==2)cut(x,y);
            else if(opt==3)
            {
                access(x);splay(x);
                val[x]=y;push_up(x);
            }
        }
    }
    inline void input()
    {
        n=read<int>();m=read<int>();
        For(i,1,n)sum[i]=val[i]=read<int>();
    }
    int main()
    {
        file();
        input();
        work();
        return 0;
    }
    
    
  • 相关阅读:
    学生管理
    数据类型
    Linux安装、发布Django项目
    函数式编程
    学生管理系统
    mysql数据库工具类
    python操作数据库
    链接mysql建库建表
    列表元组字典集合
    内置对象相关方法
  • 原文地址:https://www.cnblogs.com/dengyixuan/p/8001563.html
Copyright © 2011-2022 走看看