【学习笔记】动态树Link-Cut-Tree

这是两个月前写的,看能不能搬运过来……

 

动态树是一类维护森林连通性的问题(已纠正,感谢ZQC巨佬),目前最(wo)常(zhi)见(hui)的动态树就是LCT(Link-Cut-Tree),然而LCT似乎是处理路径的,处理子树可能力不足。据说有一种称为Top Tree数据结构,可以处理所有。但是学不动了Orz
LCT中最主要的是Access操作,Access(u)操作的含义是,从当前的节点u向他所在的根节点连一条重路径,这是相当于把沿路的重路径全都断开,重新拉一条从u到根的重路径。
makeroot(x)操作:
若想让u成为当前树的根节点,则可以先access(u),再splay(u),把u转为当前splay的根节点。因为splay维护的是深度,所以u没有右儿子(没有比u还要深的点,因为重链定义),所以换根就相当于一次区间翻转,交换左右子树即完成区间翻转。此时就可以打标记了。
所以,makeroot=access+splay+rev
还有一个isroot操作,超级简单,就是判断这是不是一条重路径的根,只要他的fa指针指向的节点的左右子树都不是他(证明此时这是一条虚边),那么这就是一棵子树的根节点。
link(x,y)操作:
在u和v之间连边,可以makeroot(u),再让u的父亲为v,这就相当于v本身是一棵splay,而u和v之间连了条轻边。
cut(u,v)操作:
断开u和v之间的边,可以先makeroot(u),再access(v),再splay(v),此时v的左儿子必定为u,于是断开v和v的左儿子即可。
至于翻转标记的传递,就是跟Splay一样就行了。
但标记下放有一个问题。因为splay是时时刻刻在分裂与合并的,因为要动态维护每条重链,所以在splay之前,要先把根节点到当前节点全部下放一遍标记,防止标记下放不完全。
split(x,y)操作:
split相当于把两个子树分开,考虑到我们cut的时候第一步也是分开,所以

$split=makeroot(u)+access(v)+splay(v)$
然后还要保存一些轻边(虚边),对于轻边我们需要单独记录处理。在原树上,当前重链的顶端节点与他的父亲的边即为轻边,如果不记录,树将是不完整的。
具体到代码实现,可以是当前splay的根节点的父亲即为当前splay上面的那个重链所在的splay上的点,但上面的splay的儿子并不指向当前splay的父亲,即为用splay的根节点的父亲来存储轻边。
动态树的主要时间消耗在Access上,而Access的时间复杂度是

$O(nlogn)$
好像最后在UOJ群里看到一句话:
*树剖能做的,动态树都能做,只不过有的东西动态树做起来比树剖烦的多*
好像超级有道理,因为我写的维护子树size,树剖的话天生自带,但是LCT每次Access跳轻重边的时候都要交换,超级烦。
下面是这几天做的一点题目:
1.洞穴勘测
裸的连通性询问。

 1 #include<bits/stdc++.h>
 2 #define N 10005
 3 #define inf 1000000007
 4 using namespace std;
 5 int n,m;
 6 struct Link_Cut_Tree{
 7     int fa[N],c[N][2],q[N],top;bool rev[N];
 8     Link_Cut_Tree(){
 9         top=0;memset(fa,0,sizeof(fa));
10         memset(c,0,sizeof(c));
11         memset(rev,0,sizeof(rev));
12     }
13     inline bool isroot(int x){return c[fa[x]][1]!=x&&c[fa[x]][0]!=x;}
14     inline void pushdown(int x){
15         int l=c[x][0],r=c[x][1];
16         if(rev[x]){
17             rev[x]^=1;rev[l]^=1;rev[r]^=1;
18             swap(c[x][1],c[x][0]);
19         }
20     }
21     void rotate(int x){
22         int y=fa[x],z=fa[y],l,r;
23         l=(c[y][1]==x);r=l^1;
24         if(!isroot(y))c[z][c[z][1]==y]=x;
25         fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
26         c[y][l]=c[x][r];c[x][r]=y;
27     }    
28     void splay(int x){
29         int top=0;q[++top]=x;
30         for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
31         while(top)pushdown(q[top--]);
32         while(!isroot(x)){
33             int y=fa[x],z=fa[y];
34             if(!isroot(y)){
35             if(c[y][0]==x^c[z][0]==y)rotate(x);else rotate(y);
36         }
37         rotate(x);
38         }
39     }
40     void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t;}
41     void makeroot(int x){access(x);splay(x);rev[x]^=1;}
42     void link(int x,int y){makeroot(x);fa[x]=y;splay(x);}
43     void split(int x,int y){makeroot(x);access(y);splay(y);}
44     void cut(int x,int y){split(x,y);c[y][0]=fa[x]=0;}
45     int find(int x){
46         access(x);splay(x);int y=x;while(c[y][0])y=c[y][0];return y;
47     }
48 }T;
49 inline int read(){
50     int x=0,f=1;char ch;
51     do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
52     do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
53     return f*x;
54 }
55 int main(){
56     char s[10];int x,y;
57     n=read();m=read();
58     for(int i=1;i<=m;i++){
59         scanf("%s",s);
60         x=read();y=read();
61         if(s[0]=='C')T.link(x,y);
62         if(s[0]=='D')T.cut(x,y);
63         if(s[0]=='Q'){
64             int xx=T.find(x),yy=T.find(y);
65             if(xx==yy)puts("Yes");else puts("No");
66         }
67     }
68     return 0;
69 }

 

2.bzoj1180 OTOCI
修改,连边,区间权值合。
其实可以先读入所有的边得到最后的树,然后离线树剖,用并查集维护边就行了
本蒟蒻还是写了Link-Cut-Tree

 1 #include<bits/stdc++.h>
 2 #define N 30005
 3 #define inf 1000000000
 4 #define yql 1000000007
 5 #define ll long long
 6 using namespace std;
 7 int q,n;
 8 struct Link_Cut_Tree{
 9     int c[N][2],fa[N],sumv[N],val[N],q[N],top[N];
10     bool rev[N];
11     void pushup(int x){
12         int l=c[x][0],r=c[x][1];sumv[x]=sumv[l]+sumv[r]+val[x];
13     }
14     void pushdown(int x){
15         int l=c[x][0],r=c[x][1];
16         if(rev[x]){
17             rev[x]^=1;rev[r]^=1;rev[l]^=1;
18             swap(c[x][1],c[x][0]);
19         }
20     }
21     bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
22     void rotate(int x){
23         int y=fa[x],z=fa[y],l,r;
24         l=(c[y][1]==x);r=l^1;
25         if(!isroot(y))c[z][c[z][1]==y]=x;
26         fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
27         c[y][l]=c[x][r];c[x][r]=y;
28         pushup(y);pushup(x);
29     }   
30     void splay(int x){
31         int top=0;q[++top]=x;
32         for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
33         while(top)pushdown(q[top--]);
34         while(!isroot(x)){
35             int y=fa[x],z=fa[y];
36             if(!isroot(y)){
37             if(c[y][0]==x^c[z][0]==y)rotate(x);else rotate(y);
38             }
39         rotate(x);
40         }
41     }
42     void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);}
43     void makeroot(int x){access(x);splay(x);rev[x]^=1;}
44     void link(int x,int y){makeroot(x);fa[x]=y;}
45     void split(int x,int y){makeroot(x);access(y);splay(y);}
46     void cut(int x,int y){split(x,y);c[y][0]=fa[x]=0;}
47     int find(int x){
48         access(x);splay(x);int y=x;while(c[y][0])y=c[y][0];return y;
49     }
50 }T;
51 inline int read(){
52     int f=1,x=0;char ch;
53     do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
54     do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
55     return f*x;
56 }
57 int main(){
58     n=read();
59     for(int i=1;i<=n;i++){T.sumv[i]=read();T.val[i]=T.sumv[i];}
60     q=read();char s[20];
61     while(q--){
62         scanf("%s",s);int x=read(),y=read();
63         if(s[0]=='b'){
64             int xx=T.find(x),yy=T.find(y);
65             if(xx==yy)puts("no");
66             else puts("yes"),T.link(x,y);
67         }
68         if(s[0]=='p')T.makeroot(x),T.val[x]=y,T.pushup(x);
69         if(s[0]=='e'){
70             int xx=T.find(x),yy=T.find(y);
71             if(xx!=yy)puts("impossible");
72             else{
73                 T.makeroot(x);T.access(y);T.splay(y);
74                 printf("%d\n",T.sumv[y]);
75             }
76         }
77     }
78     return 0;
79 }

 


3.NOI2014 膜法森林
考虑Kruskal求出最小生成树,每次加,如果当前的边成环,那么就在之前的边里找一个最大权的cut掉。
维护连通性,cut边,最大值询问,LCT很擅长啦~

 1 #include<bits/stdc++.h>
 2 #define inf 1000000007
 3 #define N 200005
 4 using namespace std;
 5 int n,m,cnt,_=inf;
 6 int f[N];
 7 int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
 8 struct Edge{
 9     int u,v,w,c;
10     bool operator<(const Edge &x)const{return w<x.w;}
11 }G[2*N];
12  
13 struct Link_Cut_Tree{
14     int c[N][2],fa[N],q[N],rev[N],top;
15     int maxv[N],val[N];
16     inline void pushup(int x){
17         int l=c[x][0],r=c[x][1];
18         maxv[x]=x;
19         if(val[maxv[l]]>val[maxv[x]])maxv[x]=maxv[l];
20         if(val[maxv[r]]>val[maxv[x]])maxv[x]=maxv[r];
21     }
22     Link_Cut_Tree(){top=0;memset(rev,0,sizeof(rev));}
23     bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
24     void pushdown(int x){
25         int l=c[x][0],r=c[x][1];
26         if(rev[x]){
27             rev[l]^=1;rev[r]^=1;rev[x]^=1;
28             swap(c[x][1],c[x][0]);
29         }
30     }
31     void rotate(int x){
32         int y=fa[x],z=fa[y],l,r;
33         l=(c[y][1])==x;r=l^1;
34         if(!isroot(y))c[z][c[z][1]==y]=x;
35         fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
36         c[y][l]=c[x][r];c[x][r]=y;
37         pushup(y);pushup(x);
38     }
39     void splay(int x){
40         int top=0;q[++top]=x;
41         for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
42         while(top)pushdown(q[top--]);
43         while(!isroot(x)){
44             int y=fa[x],z=fa[y];
45             if(!isroot(y)){
46             if(c[y][0]==x^c[z][0]==y)rotate(x);else rotate(y);
47             }
48         rotate(x);
49         }
50     }
51     void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);}
52     void makeroot(int x){access(x);splay(x);rev[x]^=1;}
53     void link(int x,int y){makeroot(x);fa[x]=y;splay(x);}
54     void split(int x,int y){makeroot(x);access(y);splay(y);}
55     void cut(int x,int y){split(x,y);c[y][0]=fa[x]=0;pushup(y);}
56     int find(int x){access(x);splay(x);int y=x;while(c[y][0])y=c[y][0];return y;}
57     int querymax(int x,int y){split(x,y);return maxv[y];}   
58 };
59 Link_Cut_Tree T;
60 inline int read(){
61     int f=1,x=0;char ch;
62     do{ch=getchar();if(ch=='0')f=-1;}while(ch<'0'||ch>'9');
63     do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
64     return f*x;
65 }
66 int main(){
67     n=read();m=read();
68     for(int i=1;i<=n+10;i++)f[i]=i;
69     for(int i=1;i<=m;i++){
70         G[i].u=read(),G[i].v=read(),G[i].w=read();G[i].c=read();
71     }
72     sort(G+1,G+m+1);
73     int tot=0;
74     for(int i=1;i<=m;i++){
75         int u=G[i].u,v=G[i].v,w=G[i].w,c=G[i].c;
76         if(find(u)==find(v)){
77              int t=T.querymax(u,v);
78              if(T.val[t]>G[i].c){
79                  T.cut(t,G[t-n].u);
80                  T.cut(t,G[t-n].v);
81              }
82              else {
83                   if(find(1)==find(n))_=min(_,G[i].w+T.val[T.querymax(1,n)]);
84                   continue;
85              }
86         }
87         else f[find(u)]=find(v);
88         T.val[n+i]=G[i].c;T.maxv[n+i]=n+i;
89         T.link(u,n+i);T.link(v,n+i);
90         if(find(1)==find(n))_=min(_,G[i].w+T.val[T.querymax(1,n)]);
91     }
92     if(_==inf)puts("-1");else printf("%d\n",_);
93     return 0;
94 }

 

4.uoj 共价大爷游长沙
%%%%毛爷爷,不看题解不会系列。
这题做的极其艰辛:
1.zcy:我怎么维护子树size啊…………啊………………
想了半个下午,我好像会了!去机房写!
2.zcy:我的权值怎么这么小啊,都是几十几十的东西……
wori,随机数种子选的有毒?我去uoj上找一个吧……
3.zcy:我的link怎么权值改的那么鬼啊?我看看……
wori,cut写错了,mdzz。
代码准确度啊……

#include<bits/stdc++.h>
#define N 300100
using namespace std;
int n,m,x,y,z;
struct Link_Cut_Tree{
    int c[N][2],q[N],fa[N],size[N],top,rev[N],val[N],sumv[N];
    int a[N],b[N],w[N],sum;
    inline bool isroot(int x){return fa[x]==0||c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    inline void pushdown(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]);
        }
    }
    void pushall(int x){
        if(!isroot(x))pushall(fa[x]);pushdown(x);
    }
    inline void pushup(int x){
        sumv[x] = val[x]^sumv[c[x][0]]^sumv[c[x][1]];
    }
    void rotate(int x){
        int y = fa[x],g=fa[y],ch=c[y][1]==x;
          if (!isroot(y))c[g][c[g][1]==y]=x;
          c[y][ch]=c[x][!ch],fa[c[y][ch]]=y;
          fa[y] =x,fa[x]=g,c[x][!ch] = y;
          pushup(y);
    }
    void splay(int x){
        int top=0;q[++top]=x;
        for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
        while(top)pushdown(q[top--]);
        while(!isroot(x)){
            int y=fa[x],z=fa[y];
            if(!isroot(y)){
            if(c[y][0]==x^c[z][0]==y)rotate(x);else rotate(y);
            }
        rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for(int t=0;x;t=x,x=fa[x]){
            splay(x);val[x]^=sumv[t]^sumv[c[x][1]];
            c[x][1]=t;pushup(x);
        }
    }
    void makeroot(int x){access(x);splay(x);rev[x]^=1;}
    void link(int x,int y){
        makeroot(x);makeroot(y);fa[x]=y;val[y]^=sumv[x];pushup(y);
    }
    void split(int x,int y){makeroot(x);access(y);splay(y);}
    void cut(int x,int y){
        makeroot(y);access(y);splay(x);fa[x]=0;val[y]^=sumv[x];pushup(y);
    }
    void change(int x,int v){access(x);splay(x);val[x]^=v;pushup(x);}
}T;

struct Edge{
    int u;int v;int w;
    Edge(int a=0,int b=0,int c=0){u=a;v=b;w=c;}
}G[5*N];
inline int read(){
    int x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int main(){
    int id;
    id=read();n=read();m=read();
    srand(time(NULL));
    for(int i=1;i<n;i++){
        x=read(),y=read();
        T.link(x,y);
    }x=0;y=0;
    int tot=0,res=0;
    while(m--){
        int opt=read(),x,y,u,v;
        if(opt==1){
            x=read();y=read();u=read();v=read();
            T.cut(x,y);T.link(u,v);//printf("%d\n",T.val[v]);
        }
        else if(opt==2){
            int k=rand();
            x=read(),y=read();while(!k)k=rand();
            G[++tot]=Edge(x,y,k);res^=k;
            T.change(x,k);T.change(y,k);
            //printf("%d\n",T.val[y]);
        }
        else if(opt==3){
            x=read();
            res^=G[x].w;
            T.change(G[x].u,G[x].w);T.change(G[x].v,G[x].w);
        }
        else if(opt==4){
            x=read();y=read();
            T.makeroot(x);T.access(y);
            if(T.val[y]==res)puts("YES");
            else puts("NO");
        }
        //printf("%d\n",res);printf("%d\n",T.val[y]);
    }
    return 0;
}

 

 

不开心,这个快把我写死的LCT居然只有100行
5.ZJOI2012 网络
这题如果单看每种颜色,我们只要建一个LCT就能轻松水过去,无非是要加个cnt
但是颜色多怎么办?考虑颜色种类很小,我们多建几个LCT不就行了么?

  1 #include<bits/stdc++.h>
  2 #define N 10005
  3 #define M 100005
  4 using namespace std;
  5 int n, m, c, k, u, v, w, op, x,val[N];
  6 struct Link_Cut_Tree{
  7     int c[N][2],fa[N],rev[N],maxv[N],cnt[N],q[N];
  8     inline void pushup(int x){
  9         maxv[x]=val[x];int l=c[x][0],r=c[x][1];
 10         if(l)maxv[x]=max(maxv[x],maxv[l]);
 11         if(r)maxv[x]=max(maxv[x],maxv[r]);
 12     }
 13     inline void pushdown(int x){
 14         int l=c[x][0],r=c[x][1];
 15         if(rev[x]){
 16             rev[l]^=1;rev[r]^=1;rev[x]^=1;
 17             swap(c[x][0],c[x][1]);
 18         }
 19     }
 20     bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
 21     void rotate(int x){
 22         int y=fa[x],z=fa[y],l,r;
 23         l=(c[y][1])==x;r=l^1;
 24         if(!isroot(y))c[z][c[z][1]==y]=x;
 25         fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
 26         c[y][l]=c[x][r];c[x][r]=y;
 27         pushup(y);pushup(x);
 28     }
 29     void splay(int x){
 30         int top=0;q[++top]=x;
 31         for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
 32         while(top)pushdown(q[top--]);
 33         while(!isroot(x)){
 34             int y=fa[x],z=fa[y];
 35             if(!isroot(y)){
 36             if(c[y][0]==x^c[z][0]==y)rotate(x);else rotate(y);
 37             }
 38         rotate(x);
 39         }
 40     }
 41     void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);}
 42     void makeroot(int x){access(x);splay(x);rev[x]^=1;}
 43     void link(int x,int y){cnt[x]++;cnt[y]++;makeroot(x);fa[x]=y;splay(x);}
 44     void split(int x,int y){makeroot(x);access(y);splay(y);}
 45     void cut(int x,int y){split(x,y);cnt[x]--;cnt[y]--;c[y][0]=fa[x]=0;pushup(y);}
 46     int find(int x){access(x);splay(x);int y=x;while(c[y][0])y=c[y][0];return y;}
 47     int querymax(int x,int y){split(x,y);return maxv[y];}    
 48 }lct[15];
 49 inline int read(){
 50     int f=1,x=0;char ch;
 51     do{ch=getchar();if(ch=='0')f=-1;}while(ch<'0'||ch>'9');
 52     do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
 53     return f*x;
 54 }
 55 struct Edge{
 56     int u,v;
 57     bool operator<(const Edge &x)const{if(u!=x.u)return u<x.u;
 58     else return v<x.v;}
 59 };
 60 map<Edge,int> mp;
 61 int main(){
 62     n=read();m=read();c=read();k=read();
 63     for(int i=1;i<=n;i++)val[i]=read();
 64     for(int i=1;i<=m;i++){
 65         int u=read(),v=read(),w=read();
 66         w++;
 67         Edge e1=(Edge){u,v},e2=(Edge){v,u};
 68         mp[e1]=mp[e2]=w;
 69         lct[w].link(u,v);
 70     }
 71     while(k--){
 72         int opt=read();
 73         if(opt==0){
 74             int x=read(),w=read();
 75             val[x]=w;
 76             for(int i=1;i<=c;i++)lct[i].splay(x);
 77         }
 78         else if(opt==1){
 79             int u=read(),v=read(),w=read();
 80             w++;
 81             Edge a=(Edge){u,v},b=(Edge){v,u};
 82             if(!mp.count(a)){
 83                 puts("No such edge.");continue;
 84             }
 85             int xxx=mp[a];
 86             if(xxx==w){
 87                 puts("Success.");continue;
 88             }
 89             if(lct[w].cnt[u]>=2||lct[w].cnt[v]>=2){
 90                 puts("Error 1.");continue;
 91             }
 92             if(lct[w].find(u)==lct[w].find(v)){
 93                 puts("Error 2.");continue;
 94             }
 95             puts("Success.");
 96             lct[xxx].cut(u,v);lct[w].link(u,v);
 97             mp[a]=mp[b]=w;
 98         }else{
 99             int w=read(),u=read(),v=read();
100             w++;
101             if(lct[w].find(u)!=lct[w].find(v)){
102                 puts("-1");continue;
103             }
104             printf("%d\n",lct[w].querymax(u,v));
105         }
106     }
107     return 0;
108 }

 

posted @ 2017-05-08 18:07  zcysky  阅读(266)  评论(0编辑  收藏  举报