Link-Cut Tree

Posted 探险家Mr.H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n",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\n");
            else printf("No\n");
        }
    }
    return 0;
}
bzoj2049

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

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

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

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

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

以上是关于Link-Cut Tree的主要内容,如果未能解决你的问题,请参考以下文章

Link-Cut Tree

SPOJQTREE7(Link-Cut Tree)

BZOJ2843极地旅行社(Link-Cut Tree)

动态树Link-Cut Tree(LCT)

QTREE5 Link-Cut Tree

BZOJ4736温暖会指引我们前行(Link-Cut Tree)