LCT动态树入门

Posted dyx_diversion

tags:

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

以上是关于LCT动态树入门的主要内容,如果未能解决你的问题,请参考以下文章

LCT(link cut tree) 动态树

关于动态树和LCT的一些学习感受(持续更新)

hdu 5398 动态树LCT

芝士:LCT

Hdu 4010-Query on The Trees LCT,动态树

LCT一步步地解释Link-cut Tree