Link-Cut Tree

来源:互联网 发布:javascript分页代码 编辑:程序博客网 时间:2024/06/07 16:17

推荐资料:

《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;}
0 3
原创粉丝点击