• Link-Cut Tree


    树剖,也被称作“静态树”是用线段树维护树上每条链的信息

    而Link-Cut Tree用Splay森林维护树上的动态信息

    先明确几个定义

    1.重儿子    这里指推广之后的重儿子,满足I,II两条性质

    I:重儿子和它的父亲在同一棵Splay中

    II:一个节点最多有一个重儿子

    2.重边    连接重儿子和它父亲的边

    3.重链    重边组成的一条链

    我们用一个Splay(这个Splay称作“辅助树”即Auxiliary Tree)来维护一条重链,这个Splay中每个点的值等于它在原树中的深度

    同时“父亲不认轻儿子只认重儿子,儿子都认父亲”

    具体体现为辅助树的根节点的父亲指向链顶的父亲节点,然而链顶的父亲节点的儿子并不指向辅助树的根节点

    这条性质方便了我们后续的操作

    注意:辅助树<>原树    辅助树的树根<>原树树根    辅助树中的父亲<>原树中的父亲

    辅助树在不断变化

    LCT所用的Splay跟普通的Splay不一样,因为是森林,所以要判断某个点是不是根节点

    同时,略去了值的维护和查找

    旋转方式也与原来有了很大的区别

    下面是两道例题

    #include<cstdio>
    #include<iostream>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int maxn=200010;
    int n,m;
    int next[maxn];
    int rt,Size;
    struct LCT
    {
        int son[maxn][2],f[maxn],val[maxn],size[maxn],cnt[maxn],rev[maxn],st[maxn];
        inline void pushup(int x)
        {
            size[x]=size[son[x][0]]+size[son[x][1]]+1;
        }
        inline int isroot(int x){return son[f[x]][0]!=x && son[f[x]][1]!=x;}
        inline void rotate(int x)
        {
            int y=f[x],z=f[y],l,r;
            if(son[y][0]==x)l=0;
            else l=1;r=l^1;
            if(!isroot(y))
            {
                if(son[z][0]==y)son[z][0]=x;
                else son[z][1]=x;
            }
            f[x]=z;f[y]=x;f[son[x][r]]=y;
            son[y][l]=son[x][r];son[x][r]=y;
            pushup(y),pushup(x);
        }
        inline void pushdown(int id)
        {
            int lch=son[id][0],rch=son[id][1];
            if(rev[id])
            {
                rev[id]^=1;rev[lch]^=1;rev[rch]^=1;
                swap(son[id][0],son[id][1]);
            }
        }
        inline void Splay(int x)
        {
            int top=0;st[++top]=x;
            for(int i=x;!isroot(i);i=f[i])st[++top]=f[i];
            for(int i=top;i;i--)pushdown(st[i]);
            while(!isroot(x))
            {
                int y=f[x],z=f[y];
                if(!isroot(y))
                {
                    if(son[y][0]==x^son[z][0]==y)rotate(x);
                    else rotate(y);
                }
                rotate(x);
            }
        }
        inline void access(int x)
        {
            int t=0;
            while(x)
            {
                Splay(x);
                son[x][1]=t;
                t=x;
                x=f[x];
            }
        }
        inline void rever(int x){access(x);Splay(x);rev[x]^=1;}
        inline void link(int x,int y){rever(x);f[x]=y;Splay(x);}
        inline void cut(int x,int y){rever(x);access(y);Splay(y);son[y][0]=f[x]=0;}
        inline int findf(int x)
        {
            access(x);Splay(x);
            int y=x;
            while(son[y][0])y=son[y][0];
            return y;
        }
    }Tree;
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
        {
            int x=read();
            Tree.f[i]=x+i;Tree.size[i]=1;
            if(Tree.f[i]>n+1)Tree.f[i]=n+1;
            next[i]=Tree.f[i];
        }
        Tree.size[n+1]=1;
        m=read();
        for(int i=1;i<=m;i++)
        {
            int op=read();
            if(op==1)
            {
                Tree.rever(n+1);
                int x=read();x++;
                Tree.access(x);Tree.Splay(x);printf("%d
    ",Tree.size[Tree.son[x][0]]);
            }
            else 
            {
                int x=read(),y=read();x++;
                int t=min(n+1,x+y);
                Tree.cut(x,next[x]);Tree.link(x,t);next[x]=t;
            }
        }
    }
    bzoj2002
    #include<cstdio>
    #include<iostream>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int maxn=200010;
    int n,m;
    int rt,Size;
    struct LCT
    {
        int son[maxn][2],f[maxn],val[maxn],size[maxn],cnt[maxn],rev[maxn],st[maxn];
        inline int isroot(int x){return son[f[x]][0]!=x && son[f[x]][1]!=x;}
        inline void rotate(int x)
        {
            int y=f[x],z=f[y],l,r;
            if(son[y][0]==x)l=0;
            else l=1;r=l^1;
            if(!isroot(y))
            {
                if(son[z][0]==y)son[z][0]=x;
                else son[z][1]=x;
            }
            f[x]=z;f[y]=x;f[son[x][r]]=y;
            son[y][l]=son[x][r];son[x][r]=y;
        }
        inline void pushdown(int id)
        {
            int lch=son[id][0],rch=son[id][1];
            if(rev[id])
            {
                rev[id]^=1;rev[lch]^=1;rev[rch]^=1;
                swap(son[id][0],son[id][1]);
            }
        }
        void Splay(int x)
        {
            int top=0;st[++top]=x;
            for(int i=x;!isroot(i);i=f[i])st[++top]=f[i];
            for(int i=top;i;i--)pushdown(st[i]);
            while(!isroot(x))
            {
                int y=f[x],z=f[y];
                if(!isroot(y))
                {
                    if(son[y][0]==x^son[z][0]==y)rotate(x);
                    else rotate(y);
                }
                rotate(x);
            }
        }
        inline void access(int x)
        {
            int t=0;
            while(x)
            {
                Splay(x);
                son[x][1]=t;
                t=x;
                x=f[x];
            }
        }
        inline void rever(int x){access(x);Splay(x);rev[x]^=1;}
        inline void link(int x,int y){rever(x);f[x]=y;Splay(x);}
        inline void cut(int x,int y){rever(x);access(y);Splay(y);son[y][0]=f[x]=0;}
        inline int findf(int x)
        {
            access(x);Splay(x);
            int y=x;
            while(son[y][0])y=son[y][0];
            return y;
        }
    }Tree;
    int main()
    {
        char ch[10];
        int x,y;
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            scanf("%s",ch);
            x=read();y=read();
            if(ch[0]=='C')Tree.link(x,y);
            else if(ch[0]=='D')Tree.cut(x,y);
            else
            {
                if(Tree.findf(x)==Tree.findf(y))printf("Yes
    ");
                else printf("No
    ");
            }
        }
        return 0;
    }
    bzoj2049

    重点讲一下一个最重要的操作:Access

    这个操作的意思是将一个点与原先的重儿子切断,并使这个原树上这个点到根路径上的边全都变为重边

    用处:使这个节点到根的路径上的所有节点形成了一棵Splay,便于操作或查询节点到根路径上的所有节点

    实现:不断把X旋到当前辅助树的根,然后它的右子树就是重儿子了,修改即可

    后面的几个操作比较显然,可以看代码,这里不赘述了

  • 相关阅读:
    iOS开发——UI基础-自定义构造方法,layoutSubviews,Xib文件,利用Xib自定义View
    iOS开发——UI基础-懒加载,plist文件,字典转模型,自定义view
    iOS开发——UI基础-按钮的创建和设置
    iOS开发——UI基础-Xcode资源拷贝
    机器学习中的数学基础_七月算法4月机器学习班第1次课程笔记
    leetcode--Merge Sorted Array
    leetcode--First Missing Positive
    leetcode--Divide two integers
    leetcode--Implement strStr()
    leetcode--Remove Element
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/7967324.html
Copyright © 2020-2023  润新知