1. 程式人生 > >LCT動態樹入門

LCT動態樹入門

put span pri 每一個 stdout 路徑 digi getch etc

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,但因為題目的不同而oush_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動態樹入門