最近学习了LCT,这种基于Splay的数据结构真是给人一种优美的感觉啊。
简介
Link Cut Tree是一种支持动态加边删边以维护动态森林的数据结构,其核心思想是将一棵树分为若干棵Splay(辅助树),通过对Splay进行操作去维护原树(路径);
类比于树链剖分中将树剖分为重链,LCT也是将树分为若干条链,其中每一条链都是一棵以节点实际深度为排序关键字的Splay,Splay的根节点指向另一棵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 }
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 }
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 }
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 }
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 }
模板(题)
2594: [Wc2006]水管局长数据加强版
Time Limit: 25 Sec Memory Limit: 128 MBSubmit: 4094 Solved: 1272
[Submit][Status][Discuss]
Description
Input
Output

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 ",ask[a].ans); 240 } 241 } 242 return 0; 243 }