Link Cut Tree学习笔记

最近学习了LCT,这种基于Splay的数据结构真是给人一种优美的感觉啊。


简介

Link Cut Tree是一种支持动态加边删边以维护动态森林的数据结构,其核心思想是将一棵树分为若干棵Splay(辅助树),通过对Splay进行操作去维护原树(路径);

类比于树链剖分中将树剖分为重链,LCT也是将树分为若干条链,其中每一条链都是一棵以节点实际深度为排序关键字的SplaySplay的根节点指向另一棵Splay

在一棵Splay(重链)中的边被为重边(偏爱边),连接两棵Splay的边为轻边,如下图,红边为重边:


基本操作

1、$ Access(x) $:访问节点$x$,其含义是打通一条从根到$x$的路径,即将这条路径上所有的边变为重边,将所有与这条路径相连的边变为轻边;

 

实现:不断将$x$旋转至Splay的根,然后断掉它原来的右儿子相连的边,改为连接$las$(上一个处理的$x$),然后处理$x$的父亲,直至到LCT的树根;

LCT的大部分操作基于$Access$,而$Access$的复杂度是均摊$logn$的,并不会证明orz,可以去看一下杨哲dalao的集训队论文

1 void access(int x)
2 {
3     for(int las=0;x;las=x,x=tree[x].fa)
4     {
5         splay(x),tree[x].son[1]=las;
6         pull_up(x);
7     }
8 }
Access

2、$ Make$_$Root(x) $:将$x$作为它所在LCT的树根;

实现:访问$x$,将$x$旋转到根,然后翻转$x$所在的Splay

关于翻转,因为这里的Splay按照节点实际深度排序,而换根改变了排序键值,因此翻转一下,把链倒过来即可;

1 void make_root(int x)
2 {
3     access(x),splay(x),reverse(x);
4 }
Make_Root

3、$ Link(i,j) $:连接$i$,$j$;

实现:将$i$作为树根,然后把$i$父亲赋值为$j$;

1 void link(int i,int j)
2 {
3     make_root(i),tree[i].fa=j;
4 }
Link

4、$ Cut(i,j) $:断开连接$i$,$j$的边;

实现:将$i$作为树根,访问$j$,将$j$旋转到根,断开$j$与其左儿子;

$i$与$j$在原树中一定直接相连,辅助树中也是如此,因此访问$j$并旋转后$i$一定是$j$的左儿子;

1 void cut(int i,int j)
2 {
3     make_root(i),access(j),splay(j);
4     tree[i].fa=tree[j].son[0]=0;
5     pull_up(j);
6 }
Cut

5、$ Find$_$Root(x) $:查询$x$所在LCT的根;

实现:访问$x$后找到它所在辅助树的第一个节点即可;

1 int find_root(int x)
2 {
3     access(x),splay(x);
4     while(tree[x].son[0])
5     {
6         x=tree[x].son[0];
7     }
8     return x;
9 }
Find_Root

模板(题)

2594: [Wc2006]水管局长数据加强版

Time Limit: 25 Sec  Memory Limit: 128 MB
Submit: 4094  Solved: 1272
[Submit][Status][Discuss]

Description

SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。

Input

输入文件第一行为3个整数:N, M, Q分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
以下M行,每行3个整数x, y和t,描述一条对应的水管。x和y表示水管两端结点的编号,t表示准备送水所需要的时间。我们不妨为结点从1至N编号,这样所有的x和y都在范围[1, N]内。
以下Q行,每行描述一项任务。其中第一个整数为k:若k=1则后跟两个整数A和B,表示你需要为供水公司寻找一条满足要求的从A到B的水管路径;若k=2,则后跟两个整数x和y,表示直接连接x和y的水管宣布报废(保证合法,即在此之前直接连接x和y尚未报废的水管一定存在)。

Output

按顺序对应输入文件中每一项k=1的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。
 
 
最后挂道题吧,LCT维护最小生成树,需要删边;
离线后变为加边,维护路径最大值即可;
AC GETDAZE
 
↓代码
  1 #include<algorithm>
  2 #include<iostream>
  3 #include<complex>
  4 #include<cstring>
  5 #include<string>
  6 #include<cstdio>
  7 #include<vector>
  8 #include<cmath>
  9 #include<queue>
 10 #include<map>
 11 #include<set>
 12 #define N 100039
 13 #define mod 20070831
 14 #define inf 0x3f3f3f3f
 15 #define ll long long
 16 using namespace std;
 17 struct question
 18 {
 19     int opt,i,j,ans;
 20 }ask[N];
 21 struct edge
 22 {
 23     int u,v,w,des;
 24 }net[N*10];
 25 struct Sinogi
 26 {
 27     int fa,son[2],w,mx,mr,rev;
 28 }tree[N*15];
 29 int n,m,q,fa[N],des[N];
 30 inline int is_root(int x)
 31 {
 32     return tree[tree[x].fa].son[0]!=x && tree[tree[x].fa].son[1]!=x;
 33 }
 34 void reverse(int x)
 35 {
 36     swap(tree[x].son[0],tree[x].son[1]);
 37     tree[x].rev^=1;
 38 }
 39 void pull_up(int x)
 40 {
 41     if(tree[tree[x].son[0]].mx>tree[tree[x].son[1]].mx)
 42     {
 43         tree[x].mx=tree[tree[x].son[0]].mx;
 44         tree[x].mr=tree[tree[x].son[0]].mr;
 45     }
 46     else
 47     {
 48         tree[x].mx=tree[tree[x].son[1]].mx;
 49         tree[x].mr=tree[tree[x].son[1]].mr;
 50     }
 51     if(tree[x].w>tree[x].mx)
 52     {
 53         tree[x].mx=tree[x].w,tree[x].mr=x-n;
 54     }
 55 }
 56 void push_down(int x)
 57 {
 58     if(tree[x].rev)
 59     {
 60         reverse(tree[x].son[0]),reverse(tree[x].son[1]);
 61         tree[x].rev^=1;
 62     }
 63 }
 64 void Push_Down(int x)
 65 {
 66     if(!is_root(x))
 67     {
 68         Push_Down(tree[x].fa);
 69     }
 70     push_down(x);
 71 }
 72 inline int get(int x)
 73 {
 74     return tree[tree[x].fa].son[1]==x;
 75 }
 76 void rotate(int x)
 77 {
 78     int fa=tree[x].fa,wh=get(x);
 79     if(!is_root(fa))
 80     {
 81         tree[tree[fa].fa].son[get(fa)]=x;
 82     }
 83     tree[x].fa=tree[fa].fa;
 84     tree[fa].son[wh]=tree[x].son[wh^1];
 85     tree[tree[fa].son[wh]].fa=fa;
 86     tree[x].son[wh^1]=fa;
 87     tree[fa].fa=x;
 88     pull_up(fa),pull_up(x);
 89 }
 90 void splay(int x)
 91 {
 92     Push_Down(x);
 93     for(int fa=tree[x].fa;!is_root(x);fa=tree[x].fa)
 94     {
 95         if(!is_root(fa))
 96         {
 97             rotate(get(fa)==get(x) ? fa : x);
 98         }
 99         rotate(x);
100     }
101     pull_up(x);
102 }
103 void access(int x)
104 {
105     for(int las=0;x;las=x,x=tree[x].fa)
106     {
107         splay(x),tree[x].son[1]=las;
108         pull_up(x);
109     }
110 }
111 void make_root(int x)
112 {
113     access(x),splay(x),reverse(x);
114 }
115 void link(int i,int j)
116 {
117     make_root(i),tree[i].fa=j;
118 }
119 void cut(int i,int j)
120 {
121     make_root(i),access(j),splay(j);
122     tree[i].fa=tree[j].son[0]=0;
123     pull_up(j);
124 }
125 int query_max(int i,int j)
126 {
127     make_root(i),access(j),splay(i);
128     return tree[i].mr;
129 }
130 bool cmp_u_v(edge a,edge b)
131 {
132     return a.u==b.u ? a.v<b.v : a.u<b.u;
133 }
134 bool cmp_w(edge a,edge b)
135 {
136     return a.w<b.w;
137 }
138 int search(int i,int j)
139 {
140     int l,r,L,R,mid,res;
141     l=1,r=m;
142     while(l<=r)
143     {
144         mid=l+r>>1;
145         if(net[mid].u>=i) r=mid-1,L=mid;
146         else l=mid+1;
147     }
148     l=1,r=m;
149     while(l<=r)
150     {
151         mid=l+r>>1;
152         if(net[mid].u<=i) l=mid+1,R=mid;
153         else r=mid-1;
154     }
155     while(L<=R)
156     {
157         mid=L+R>>1;
158         if(net[mid].v>=j) R=mid-1,res=mid;
159         else L=mid+1;
160     }
161     return res;
162 }
163 int find(int k)
164 {
165     return fa[k]==k ? k : fa[k]=find(fa[k]);
166 }
167 void Kruskal()
168 {
169     sort(net+1,net+m+1,cmp_w);
170     for(int a=1;a<=n;a++)
171     {
172         fa[a]=a;
173     }
174     for(int a=1,b,c;a<=m;a++)
175     {
176         tree[n+a].w=net[a].w;
177         if(net[a].des)
178         {
179             des[net[a].des]=a;
180         }
181         else
182         {
183             b=find(net[a].u),c=find(net[a].v);
184             if(b!=c)
185             {
186                 fa[c]=b;
187                 link(net[a].u,n+a),link(n+a,net[a].v);
188             }
189         }
190     }
191 }
192 int main()
193 {
194     int stp;
195     scanf("%d%d%d",&n,&m,&q);
196     for(int a=1;a<=m;a++)
197     {
198         scanf("%d%d%d",&net[a].u,&net[a].v,&net[a].w);
199         if(net[a].u>net[a].v)
200         {
201             swap(net[a].u,net[a].v);
202         }
203     }
204     sort(net+1,net+m+1,cmp_u_v);
205     for(int a=1;a<=q;a++)
206     {
207         scanf("%d%d%d",&ask[a].opt,&ask[a].i,&ask[a].j);
208         if(ask[a].opt==2)
209         {
210             if(ask[a].i>ask[a].j)
211             {
212                 swap(ask[a].i,ask[a].j);
213             }
214             net[search(ask[a].i,ask[a].j)].des=a;
215         }
216     }
217     Kruskal();
218     for(int a=q;a>=1;a--)
219     {
220         stp=query_max(ask[a].i,ask[a].j);
221         if(ask[a].opt==1)
222         {
223             ask[a].ans=net[stp].w;
224         }
225         else
226         {
227             if(net[des[a]].w<net[stp].w)
228             {
229                 make_root(net[stp].u),access(net[stp].v),splay(n+stp);
230                 cut(net[stp].u,n+stp),cut(n+stp,net[stp].v);
231                 link(ask[a].i,n+des[a]),link(n+des[a],ask[a].j);
232             }
233         }
234     }
235     for(int a=1;a<=q;a++)
236     {
237         if(ask[a].opt==1)
238         {
239             printf("%d\n",ask[a].ans);
240         }
241     }
242     return 0;
243 }
bzoj2594

 

posted @ 2018-01-02 11:29  Sinogi  阅读(400)  评论(0编辑  收藏  举报