Link-Cut Tree

推薦資料:

《SPOJ375 QTREE 解法的一些研究》by Yang Zhe

《link cut tree》by popoqqq

正文:

LCT 是解決動態樹問題的一種數據結構

LCT=樹鏈剖分+splay

LCT利用splay來維護樹上的樹鏈,但是樹鏈不能再以size來剖分了,否則樹是靜態的。

LCT利用Access操作,將需要訪問的節點合併成一個splay,再進行操作。

在splay中是以點的深度作爲關鍵字的。

爲了更好維護這一堆splay,需要引入一個叫做Auxiliary Tree(輔助樹)的東西。
這裏寫圖片描述

在輔助樹中,如果兩個節點u,v,u認爲v是父親,而v不認u是兒子,那麼v就是一個splay的根,這條邊就叫虛邊。

所以這就解釋了下面Access中爲什麼只更新父親節點的兒子信息,而沒有更新父親節點原來Preferred Child的父親信息(這一點很精妙,仔細體會)。
具體函數

Access(x):這是一切操作的根源,把x到根的路徑上的所有點都合成一個splay,方便操作。

make_root(x):將x設爲原樹的根
make_root(5)就像如圖所示:
make_root(5)

可以發現,除了5到根的路徑上的節點深度發生了翻轉,別的節點深度相對關係是不變的,所以在splay上將5到根的路徑的所有點翻轉一下。

find(x):找到x的根。由於是按深度爲關鍵字,所以把x旋成x所在的splay的根,再一直向左兒子找,沒有了左兒子之後這個點就是根。

Link(x,y):連接x,y。先將x旋成原樹的根,再把fa[x]設成y(注意x的兒子不用更新,這是一條虛邊)。

Cut(x,y):斷開x,y的連邊。先將x旋成原樹的根,再Access(y),Splay(y),於是x在y左兒子,切斷。

維護其他x到y路徑上的信息,只要make_root(x),Access(y),Splay(y)就好了。

代碼

就拿hdu4010 Query on the tree了:

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int INF=2000000000;
const int M=300005;
int n;
int tag[M],rev[M],c[M][2],fa[M],mx[M],v[M];
struct Edge{
    int to,nxt;
}edge[M<<1];
int T,head[M],stk[M];
void init(){
    for(int i=0;i<=n;i++)
        tag[i]=rev[i]=fa[i]=c[i][0]=c[i][1]=0,head[i]=-1;
    mx[0]=-INF;T=0;
}
void add_edge(int a,int b){
    edge[T]=(Edge){b,head[a]};
    head[a]=T++;
    edge[T]=(Edge){a,head[b]};
    head[b]=T++;
}
void rec(int x,int f){
    fa[x]=f;
    for(int i=head[x];~i;i=edge[i].nxt)
        if(edge[i].to!=f)
            rec(edge[i].to,x);
}
struct Link_Cut_Tree{
    bool is_root(int x){
        return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
    }
    void push_up(int x){
        int l=c[x][0],r=c[x][1];
        mx[x]=max(mx[l],mx[r]);
        mx[x]=max(mx[x],v[x]);
    }
    void update(int x,int w){
        tag[x]+=w;
        mx[x]+=w;
        v[x]+=w;
    }
    void push_down(int x){
        int l=c[x][0],r=c[x][1];
        if(rev[x]){
            rev[l]^=1;
            rev[r]^=1;
            rev[x]^=1;
            swap(c[x][0],c[x][1]);
        }
        if(tag[x]){
            if(l) update(l,tag[x]);
            if(r) update(r,tag[x]);
            tag[x]=0;
        }
    }
    void Rotate(int x){
        int y=fa[x],z=fa[y],l,r;
        l=(c[y][1]==x);
        r=l^1;
        if(!is_root(y)) c[z][c[z][1]==y]=x;
        fa[x]=z;
        fa[y]=x;
        fa[c[x][r]]=y;
        c[y][l]=c[x][r];
        c[x][r]=y;
        push_up(y);push_up(x);
    }
    void Splay(int x){
        int top=0;
        stk[++top]=x;
        for(int i=x;!is_root(i);i=fa[i])
            stk[++top]=fa[i];
        while(top) push_down(stk[top--]);
        while(!is_root(x)){
            int y=fa[x];
            int z=fa[y];
            if(!is_root(y)){
                if(c[y][0]==x^c[z][0]==y) Rotate(x);
                else Rotate(y);
            }
            Rotate(x);
        }
    }
    void solve(int x,int y){
        make_root(x);
        Access(y);
        Splay(y);
    }
    void Access(int x){
        for(int t=0;x;t=x,x=fa[x])
            Splay(x),c[x][1]=t,push_up(x);
    }   
    void make_root(int x){
        Access(x);
        Splay(x);
        rev[x]^=1;
    }
    void link(int x,int y){
        make_root(x);
        fa[x]=y;
    }
    void cut(int x,int y){
        make_root(x);
        Access(y);
        Splay(y);
        c[y][0]=fa[c[y][0]]=0;
        push_up(y);
    }
    int find(int x){
        Access(x);
        Splay(x);
        while(c[x][0]) x=c[x][0];
        return x;
    }
    void add(int x,int y,int val){
        make_root(x);
        Access(y);
        Splay(y);
        tag[y]+=val;
        mx[y]+=val;
        v[y]+=val;
    }
}lct;
inline void Rd(int&res){
    res=0;char c;
    while(c=getchar(),!isdigit(c));
    do res=(res<<1)+(res<<3)+(c^48);
    while(c=getchar(),isdigit(c));
}
int main(){
    while(scanf("%d",&n)!=EOF){
        init();
        for(int i=1;i<n;i++){
            int a,b;
            Rd(a);Rd(b);
            add_edge(a,b);
        }
        for(int i=1;i<=n;i++)
            Rd(v[i]),mx[i]=v[i];
        rec(1,0);
        int m;
        scanf("%d",&m);
        while(m--){
            int opt,x,y;
            Rd(opt);Rd(x);Rd(y);
            if(opt==1){
                if(lct.find(x)==lct.find(y)) puts("-1");
                else lct.link(x,y);
            }else if(opt==2){
                if(lct.find(x)!=lct.find(y)||x==y) puts("-1");
                else lct.cut(x,y);
            }else if(opt==3){
                int z=x;
                x=y;
                Rd(y);
                if(lct.find(x)!=lct.find(y)) puts("-1");
                else lct.add(x,y,z);
            }else{
                if(lct.find(x)!=lct.find(y)) puts("-1");
                else{
                    lct.solve(x,y);
                    printf("%d\n",mx[y]);
                }
            }
        }
        puts("");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章