LCT学习笔记

最近自学了一下LCT(Link-Cut-Tree),参考了Saramanda及Yang_Zhe等众多大神的论文博客,对LCT有了一个初步的认识,LCT是一种动态树,可以处理动态问题的算法。对于树分治中的树链剖分,只能处理静态的数据或者在轻重链上的边或点的权值,对于其他动态的处理就毫无办法了。因此我们就需要引入LCT这个东西。那么问题来了,LCT到底是什么呢?我弄了很久总算是理解了LCT,打算总结一下LCT的基本操作。

①浅谈对LCT的初步印象

LCT用来维护动态的森林,以及一些链上操作,是处理节点无序的有根树组成的森林,进行一些列操作(例如合并,剪切,翻转,更新·····)

对于一棵树的操作,我们可以用splay维护;同理,对于一片森林,也可以用多棵splay维护,而LCT就是要把这些森林的splay联系在一起。而LCT的核心就是access操作啦!

看完之后我们知道,LCT和静态的树链剖分很像。怎么说呢?这两种树形结构都是由若干条长度不等的“重链”和“轻边”构成(名字可以不同,大概就是这个意思),“重链”之间由”轻边”连接。就像这样:

可以想象为一棵树被人为的砍成了一段段。

  LCT和树链剖分不同的是,树链剖分的链是不会变化的,所以可以很方便的用线段树维护。但是,既然是动态树,那么树的结构形态将会发生改变,所以我们要用更加灵活的维护区间的结构来对链进行维护,不难想到Splay可以胜任。如何分离树链也是保证时间效率的关键(链的数量和长度要平衡),树链剖分的“重儿子”就体现了前人博大精深的智慧。

  在这里解释一下为什么要把树砍成一条条的链:我们可以在logn的时间内维护长度为n的区间(链),所以这样可以极大的提高树上操作的时间效率。在树链剖分中,我们把一条条链放到线段树上维护。但是LCT中,由于树的形态变化,所以用能够支持合并、分离、翻转等操作的Splay维护LCT的重链(注意,单独一个节点也算是一条重链)。

  这时我们注意到,LCT中的轻边信息变得无法维护。为什么呢?因为Splay只维护了重链,没有维护重链之间的轻边;而LCT中甚至连根都可以不停的变化,所以也没法用点权表示它父边的边权(父亲在变化)。所以,如果在LCT中要维护边上信息,个人认为最方便的方法应该是把边变成一个新点和两条边。这样可以把边权的信息变成点权维护,同时为了不影响,把真正的树上节点的点权变成0,就可以用维护点的方式维护边。

②再谈LCT的各种操作

  LCT中用Splay维护链,这些Splay叫做“辅助树“。辅助树以它上面每个节点的深度为关键字维护,就是辅助树中每个节点左儿子的深度小于当前节点的深度,当前节点的深度小于右儿子的深度。

  可以把LCT认为是一个由Splay组成的森林,就像这样:(三角形代表一棵Splay,对应着LCT上一条链)

箭头是什么意思呢?箭头记录着某棵Splay对应的链向上由轻边连着哪个节点,可以想象为箭头指向“Splay 的父亲”。但是,Splay的父亲并不记录有这个儿子,即箭头是单向的。同时,每个节点要记录它是否是它所在的Splay的根。这样,Splay构成的森林就建成了。

这个是我的Splay节点最基本的定义:(如果要维护更多信息就像Splay维护区间那样加上更多标记)

1 struct node{
2     int fa,ch[2]; //父亲和左右儿子。
3     bool reverse,is_root;   //区间反转标记、是否是所在Splay的根
4 }T[maxn];

LCT中基本的Splay上操作:

 1 int getson(int x){
 2     return x==T[T[x].fa].ch[1];
 3 }
 4 void pushreverse(int x){
 5     if(!x)return;
 6     swap(T[x].ch[0],T[x].ch[1]);
 7     T[x].reverse^=1;
 8 }
 9 void pushdown(int x){
10     if(T[x].reverse){
11         pushreverse(T[x].ch[0]);
12         pushreverse(T[x].ch[1]);
13         T[x].reverse=false;
14     }
15 }
16 void rotate(int x){
17     if(T[x].is_root)return;
18     int k=getson(x),fa=T[x].fa;
19     int fafa=T[fa].fa;
20     pushdown(fa);pushdown(x);    //先要下传标记
21     T[fa].ch[k]=T[x].ch[k^1];
22     if(T[x].ch[k^1])T[T[x].ch[k^1]].fa=fa;
23     T[x].ch[k^1]=fa;
24     T[fa].fa=x;
25     T[x].fa=fafa;
26     if(!T[fa].is_root)T[fafa].ch[fa==T[fafa].ch[1]]=x;
27     else T[x].is_root=true,T[fa].is_root=false;
28     //update(fa);update(x);    //如果维护了信息,就要更新节点
29 }
30 void push(int x){
31     if(!T[x].is_root)push(T[x].fa);
32     pushdown(x);
33 }
34 void Splay(int x){
35     push(x);   //在Splay到根之前,必须先传完反转标记
36     for(int fa;!T[x].is_root;rotate(x)){
37         if(!T[fa=T[x].fa].is_root){
38             rotate((getson(x)==getson(fa))?fa:x);
39         }
40     }
41 }

access操作:

这是LCT最核心的操作。其他所有操作都要用到它。

他的含义是”访问某节点“。作用是:对于访问的节点x,打通一条从树根(真实的LCT树)到x的重链;如果x往下是重链,那么把x往下的重边改成轻边。可以理解为专门开辟一条x到根的路径,由一棵Splay维护这条路径。

access之前:(粗的是重链)

access之后:

access实现的方式很简单:先把x旋转到所在Splay的根,然后把x的右孩子的is_root设为true(此时右孩子对应的是x下方的重链,这样就断开了x和下方的重链)。用y记录上一次的x(初始化y=0),把y接到x的右孩子上,这样就把上一次的重链接到了当前重链一起,同时记得T[y].is_root=false。记录y=x,然后x=T[x].fa,把x上提。重复上面的步骤直到x=0。

实现代码如下:

 1 void access(int x){
 2     int y=0;
 3     do{
 4         Splay(x);
 5         T[T[x].ch[1]].is_root=true;
 6         T[T[x].ch[1]=y].is_root=false;
 7         //update(x);    //如果维护了信息记得更新。
 8         x=T[y=x].fa;
 9     }while(x);
10 }

mroot操作:

  这个操作的作用是把某个节点变成树根(这里的根指的是整棵LCT的根)。加上access操作,就可以方便的提取出LCT上两点之间的路径。提取u到v的路径只需要mroot(u),access(v),然后v所在的Splay对应的链就是u到v的路径。

mroot实现的方式:

  由于LCT是Splay组成的森林,所以要把x变成根就只需要让所有Splay的父亲最终指向x所在Splay。所以先access(x),Splay(x),把现在的根和将成为根的x链在一棵Splay中,并转到根即可。但是我们注意到,由于x成为了新的根,所以它和原来的根所在的Splay中深度作为关键字的性质遭到了破坏:新根x应该是Splay中深度最小的,但是之前的操作并不会改变x的深度(也就是目前x依旧是当前Splay中深度最深的)。所以,我们需要把所在的这棵Splay翻转过来。

(粗的是重链,y是原来的根)

翻转前:

翻转后:

这时候x才真正变成了根。

实现代码如下:

1 void mroot(int x){
2     access(x);
3     Splay(x);
4     pushreverse(x);
5 }

link操作:

这个操作的作用是连接两棵LCT。对于link(u,v),表示连接u所在的LCT和v所在的LCT;

link实现的方式:

很简单,只需要先mroot(u),然后记录T[u].fa=v就可以了,就是把一个Splay森林连到另一个上。

实现代码如下:

void link(int u,int v){
    mroot(u);
    T[u].fa=v;
}

cut操作:

  这个操作的作用是分离出两棵LCT。

实现代码如下:

1 void cut(int u,int v)
2     mroot(u);   //先把u变成根
3     access(v);Splay(v);    //连接u、v
4     pushdown(v);     //先下传标记
5     T[u].fa=T[v].ch[0]=0;
6     //v的左孩子表示v上方相连的重链
7     //update(v);  //记得维护信息
8 }

以上这些就是LCT的基本操作啦。

③例题分析

先来一道简单的入门题

Query on a tree

Time Limit: 851MS

Memory Limit: 1572864KB

64bit IO Format: %lld & %llu

Submit Status

Description

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.

We will ask you to perfrom some instructions of the following form:

  • CHANGE i ti : change the cost of the i-th edge to ti or
  • QUERY a b : ask for the maximum edge cost on the path from node a to node b

Input

The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.

For each test case:

  • In the first line there is an integer N (N <= 10000),
  • In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between ab of cost c (c <= 1000000),
  • The next lines contain instructions "CHANGE i ti" or "QUERY a b",
  • The end of each test case is signified by the string "DONE".

There is one blank line between successive tests.

Output

For each "QUERY" operation, write one integer representing its result.

Example

Input:
1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE

Output:
1
3

Hint

Thanh-Vy Hua Date: 2005-06-08 Time limit: 0.851s Source limit: 15000B Memory limit: 1536MB Cluster: Cube (Intel G860) Languages: ADA ASM BASH BF C C# C++ 5 CLPS LISP sbcl LISP clisp D FORT HASK ICON ICK JAVA LUA NEM NICE CAML PAS gpc PAS fpc PERL PHP PIKE PRLG PYTH 2.7 RUBY SCM qobi SCM guile ST TEXT WSPC

Submit Status

咦,这道题不是树链剖分的入门题么?  对的,不过,也可以用LCT做(树链剖分只处理静态的树和有关轻重边的动态更新,而LCT就是用来解决动态树问题)

题意:

给定一棵树,给定每条边a,b,w  (w权值)

完成两个操作:

1.把第i条边权值改成w

2.查询u到v路径上的最大权值

介绍一下access操作:查询根到u这条路径,并把这条路径更新为偏爱路径(之前的偏爱边可能会有改变)被访问到的u,不再有偏爱儿子。

每次access访问,都会把这条路径上的点用一棵splay维护,splay维护的关键词是点的深度,保证左边的子树比当前点深度小,右边的子树比当前点深度大。

并且这棵树把u作为根节点之后不再有右儿子(u没有偏爱儿子).

而splay要维护的是一条偏爱路径上的点。所有的splay会构成一个森林的集合。

要注意的是,在rotate判断ff的时候,有可能ff的左右儿子都不是f,然而没有进行判断就会跑出神奇的错误。

实现代码如下:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<queue>
  6 #include<cmath>
  7 #include<algorithm>
  8 using namespace std;
  9 /*
 10 LCT的思想类似于轻重链剖分 
 11 是用偏爱路径划分,用splay维护一条偏爱路径,然后像森林操作一样搞 
 12 u-v边的权值记录在v点上 
 13 */
 14 #define maxn 10005
 15 int n;
 16 int fa[maxn];
 17 struct edge
 18 {
 19     int v,w;
 20     edge *next;
 21     edge(int _v,int _w,edge *_next)
 22     {
 23         v=_v;
 24         w=_w;
 25         next=_next;
 26     }
 27 }*head[maxn];
 28 struct node
 29 {
 30     node *f;
 31     node *ch[2];
 32     bool root;//是否是所在辅助树的根节点 
 33     int cost;
 34     int maxcost;
 35     
 36 }tree[maxn],*null,Tnull;
 37 void init(node *u)
 38 {
 39     u->f=u->ch[0]=u->ch[1]=null;
 40     u->root=true;
 41     u->cost=u->maxcost=0;
 42 }
 43 void pushup(node *p)
 44 {
 45     p->maxcost=max(max(p->ch[0]->maxcost,p->ch[1]->maxcost),p->cost);
 46 }
 47 void rotate(node *u)
 48 {
 49     node *f=u->f;    
 50     node *ff=f->f;
 51     int d=u==f->ch[1];
 52     
 53     f->ch[d]=u->ch[d^1];
 54     if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
 55     
 56     u->f=ff;
 57     if(ff!=null)
 58     {
 59         if(ff->ch[0] == f)
 60         {
 61             ff->ch[0] = u;
 62         }
 63         else if(ff->ch[1] == f)//一定要用Else If,如果直接用Else会出现错误,因为树本身可能不是二叉树,虽然生成的Splay Tree是
 64         {
 65             ff->ch[1] = u;
 66         }
 67     }
 68     
 69     u->ch[d^1]=f;
 70     f->f=u;
 71     
 72     pushup(f);
 73     pushup(u);
 74     if(f->root)
 75     {
 76         u->root=true;
 77         f->root=false;
 78     }
 79 }
 80 void splay(node *u)
 81 {
 82     if(u==null)return ;
 83     while(!u->root)
 84     {
 85         node *f=u->f;        
 86         if(f->root)
 87         {
 88             rotate(u);
 89         }
 90         else
 91         {
 92             node *ff=f->f;
 93             int d=u==f->ch[1];
 94             int dd=f==ff->ch[1];
 95             if(d==dd)rotate(f);
 96             else rotate(u);
 97             rotate(u);
 98         }        
 99     }
100     pushup(u);
101 }
102 vector<pair<int,int> >E;
103 char ss[20];
104 void access(node *u,bool flag)
105 {
106     node *v=null;
107     while(u!=null)
108     {
109         splay(u);
110         if(flag)
111         {
112             if(u->f==null)
113             {
114                 printf("%d\n",max(u->ch[1]->maxcost,v->maxcost));
115             }
116         }
117         u->ch[1]->root=true;
118         u->ch[1]=v;
119         v->root=false;
120         pushup(u);
121         v=u;
122         u=u->f;
123     }    
124 }
125 void query(int u,int v)
126 {
127     access(tree+u,0);
128     access(tree+v,1);
129 }
130 void change(int u,int w)
131 {
132     access(tree+u,0);
133     splay(tree+u);
134     tree[u].cost=w;
135     pushup(tree+u);
136 }
137 void bfs()
138 {
139     memset(fa,-1,sizeof(fa));
140     queue<int >q;
141     q.push(1);
142     fa[1]=0;
143     while(!q.empty())
144     {
145         int u=q.front();
146         q.pop();
147         for(edge *i=head[u];i;i=i->next)
148         {
149             if(fa[i->v]==-1)
150             {
151                 fa[i->v]=u;
152                 tree[i->v].f=tree+u;
153                 tree[i->v].cost=tree[i->v].maxcost=i->w;
154                 q.push(i->v);
155             }
156         }
157     }
158 }
159 int main()
160 {
161     int T;
162     scanf("%d",&T);
163     null=&Tnull;
164     init(null);
165     while(T--) 
166     {
167         scanf("%d",&n);
168         for(int i=1;i<=n;i++)
169         {
170             head[i]=NULL;
171             init(&tree[i]);
172         }
173         E.clear();
174         for(int i=1;i<n;i++)
175         {
176             int a,b,c;
177             scanf("%d%d%d",&a,&b,&c);
178             head[a]=new edge(b,c,head[a]);
179             head[b]=new edge(a,c,head[b]);
180             E.push_back(make_pair(a,b));
181         }
182         bfs();
183         while(scanf("%s",ss)!=EOF)
184         {
185             if(ss[0]=='D')break;
186             if(ss[0]=='Q')
187             {
188                 int u,v;
189                 scanf("%d%d",&u,&v);
190                 query(u,v);
191             }
192             else if(ss[0]=='C')
193             {
194                 int a,w;
195                 scanf("%d%d",&a,&w);
196                 a--;
197                 int u=E[a].first;
198                 int v=E[a].second;
199                 if(fa[u]==v)
200                 {
201                     change(u,w);
202                 }
203                 if(fa[v]==u)
204                 {
205                     change(v,w);
206                 }
207             }
208         }
209     }
210     return 0;
211 }

一道入门题怎么够呢?那就再来一道操作多一点的~~~

Query on The Trees

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) Total Submission(s): 6250    Accepted Submission(s): 2504

Problem Description

We have met so many problems on the tree, so today we will have a query problem on a set of trees. There are N nodes, each node will have a unique weight Wi. We will have four kinds of operations on it and you should solve them efficiently. Wish you have fun!

Input

There are multiple test cases in our dataset. For each case, the first line contains only one integer N.(1 ≤ N ≤ 300000) The next N‐1 lines each contains two integers x, y which means there is an edge between them. It also means we will give you one tree initially. The next line will contains N integers which means the weight Wi of each node. (0 ≤ Wi ≤ 3000) The next line will contains an integer Q. (1 ≤ Q ≤ 300000) The next Q lines will start with an integer 1, 2, 3 or 4 means the kind of this operation. 1. Given two integer x, y, you should make a new edge between these two node x and y. So after this operation, two trees will be connected to a new one. 2. Given two integer x, y, you should find the tree in the tree set who contain node x, and you should make the node x be the root of this tree, and then you should cut the edge between node y and its parent. So after this operation, a tree will be separate into two parts. 3. Given three integer w, x, y, for the x, y and all nodes between the path from x to y, you should increase their weight by w. 4. Given two integer x, y, you should check the node weights on the path between x and y, and you should output the maximum weight on it.

Output

For each query you should output the correct answer of it. If you find this query is an illegal operation, you should output ‐1. You should output a blank line after each test case.

Sample Input

5

1 2

2 4

2 5

1 3

1 2 3 4 5

6

4 2 3

2 1 2

4 2 3

1 3 5

3 2 1 4

4 1 4

Sample Output

3

-1

7

Hint

We define the illegal situation of different operations: In first operation: if node x and y belong to a same tree, we think it's illegal. In second operation: if x = y or x and y not belong to a same tree, we think it's illegal. In third operation: if x and y not belong to a same tree, we think it's illegal. In fourth operation: if x and y not belong to a same tree, we think it's illegal.

Source

The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=4010

题意:

给定一棵树,维护4个操作:

1.给定u,v,如果不在一棵树上,它们之间连边。

2.给定u,v如果u!=v且在同一棵树上,把u设为整棵树的根,再把v和它的父亲断开。

3.给定u,v,w,如果u,v在同一条树上,把他们之间的点的权值+w

4.给定u,v,如果u,v在同一棵树上,求出他们路径上的最大值。

违法操作输出-1

思路:

连边操作,访问u,把u旋到根,把它左右翻转即可(翻转前splay上u一定没有右节点(没有偏爱儿子),翻转后一定没有左儿子,说明它就是从根到u路径上深度最小的了,就成为了根),把它接到v上,设为整棵树的根:一样的先access(u),splay(u)再rev(u)。

断开:access(u),splay(u),但不翻转,这样u没有右儿子但有左儿子,和左子树断开即可。

对于3、4操作,可以先把u设为根,再access(v)然后找到u-v这条路径上splay的根节点在哪里,信息都记录在那个节点上!

实现代码如下:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 const int maxn=300000+20;
 10 const int inf=0x3f3f3f3f;
 11 struct node
 12 {
 13     node *f;
 14     node *ch[2];
 15     bool rev;
 16     int add;
 17     int mm;
 18     int key;
 19 }tree[maxn],*null,*cur;
 20 void init()
 21 {
 22     null=tree;
 23     null->f=null->ch[0]=null->ch[1]=null;
 24     null->rev=0;
 25     null->add=0;
 26     null->mm=null->key=-inf;
 27     cur=tree+1;
 28 }
 29 node *newnode(int key)
 30 {
 31     cur->f=cur->ch[0]=cur->ch[1]=null;
 32     cur->mm=cur->key=key;
 33     cur->add=0;
 34     cur->rev=0;
 35     return cur++;
 36 }
 37 bool isroot(node *x)
 38 {
 39     return x==null||x->f->ch[0]!=x&&x->f->ch[1]!=x;
 40 }
 41 void pushup(node *u)
 42 {
 43     u->mm=max(u->key,max(u->ch[0]->mm,u->ch[1]->mm));
 44 }
 45 void pushdown(node *u)
 46 {
 47     if(u==null)return ;
 48     if(u->rev)
 49     {
 50         swap(u->ch[0],u->ch[1]);
 51         if(u->ch[0]!=null)u->ch[0]->rev^=1;
 52         if(u->ch[1]!=null)u->ch[1]->rev^=1;
 53         u->rev=0;
 54     }
 55     if(u->add)
 56     {
 57         if(u->ch[0]!=null)
 58         {
 59             u->ch[0]->add+=u->add;
 60             u->ch[0]->mm+=u->add;
 61             u->ch[0]->key+=u->add;
 62         }
 63         if(u->ch[1]!=null)
 64         {
 65             u->ch[1]->add+=u->add;
 66             u->ch[1]->mm+=u->add;
 67             u->ch[1]->key+=u->add;
 68         }
 69         u->add=0;
 70     }
 71 }
 72 void rotate(node *u)
 73 {
 74     node *f=u->f;    
 75     node *ff=f->f;
 76     int d=u==f->ch[1];
 77     
 78     if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
 79     f->ch[d]=u->ch[d^1];
 80     
 81     u->f=ff;
 82     if(ff!=null)
 83     {
 84         if(f==ff->ch[0])ff->ch[0]=u;
 85         else if(f==ff->ch[1])ff->ch[1]=u;
 86     }
 87     
 88     u->ch[d^1]=f;
 89     f->f=u;
 90     
 91     pushup(f);
 92     pushup(u);
 93 }
 94 node *sta[maxn];
 95 int cnt;
 96 void splay(node *u)
 97 {
 98     if(u==null)return ;
 99     cnt=1;
100     sta[0]=u;
101     for(node *y=u;!isroot(y);y=y->f)
102     {
103         sta[cnt++]=y->f;
104     }
105     while(cnt)pushdown(sta[--cnt]);
106     while(!isroot(u))
107     {
108         node *f=u->f;
109         node *ff=f->f;
110         if(isroot(f))
111         {
112             rotate(u);
113         }
114         else
115         {
116             int d=u==f->ch[1];
117             int dd=f==ff->ch[1];
118             if(d==dd)rotate(f);
119             else rotate(u);
120             rotate(u);
121         }
122     }
123     pushup(u);
124 }
125 node *access(node *u)
126 {
127     node *v=null;
128     while(u!=null)
129     {
130         splay(u);
131         v->f=u;
132         u->ch[1]=v;
133         pushup(u);
134         v=u;
135         u=u->f;
136     }
137     return v;
138 }
139 void link(node *u,node *v)
140 {
141     access(u);
142     splay(u);
143     u->rev=1;
144     u->f=v;
145 }
146 bool sam(node *u,node *v)
147 {
148     while(u->f!=null)u=u->f;
149     while(v->f!=null)v=v->f;
150     return u==v;
151 }
152 void changeroot(node *u)
153 {
154     access(u)->rev^=1;
155 }
156 void cut(node *u)
157 {
158     access(u);
159     splay(u);//access+旋转之后左子树的点都比u小,一定会有u的父亲 
160     u->ch[0]=u->ch[0]->f=null;
161     pushup(u);
162 }
163 node *getroot(node *u)
164 {
165     access(u);
166     splay(u);
167     while(u->f!=null)u=u->f;
168     splay(u);
169     return u;
170 }
171 int n,m;
172 int det[maxn];
173 struct edge
174 {
175     int u,v;
176 }E[maxn];
177 int main()
178 {
179     while(scanf("%d",&n)!=EOF)
180     {
181         init();
182         for(int i=1;i<n;i++)
183         {
184             scanf("%d%d",&E[i].u,&E[i].v);
185         }
186         for(int i=1;i<=n;i++)
187         {
188             scanf("%d",&det[i]);
189             newnode(det[i]);
190         }
191         for(int i=1;i<n;i++)
192         {
193             link(tree+E[i].u,tree+E[i].v);
194         }
195         scanf("%d",&m);
196         for(int i=1;i<=m;i++)
197         {
198             int k,a,b;
199             scanf("%d",&k);
200             if(k==1)
201             {
202                 scanf("%d%d",&a,&b);
203                 if(sam(tree+a,tree+b))printf("-1\n");
204                 else
205                 {
206                     link(tree+a,tree+b);
207                 }
208             }
209             else if(k==2)
210             {
211                 scanf("%d%d",&a,&b);
212                 if(a==b||!sam(tree+a,tree+b))printf("-1\n");
213                 else
214                 {
215                     changeroot(tree+a);
216                     cut(tree+b);
217                 }
218             }
219             else if(k==3)
220             {
221                 int w;
222                 scanf("%d%d%d",&w,&a,&b);
223                 if(!sam(tree+a,tree+b))printf("-1\n");
224                 else
225                 {
226                     changeroot(tree+a);
227                     access(tree+b);
228                     node *q=getroot(tree+b);
229                     q->add+=w;
230                     q->mm+=w;
231                     q->key+=w;
232                 }
233             }
234             else if(k==4)
235             {
236                 scanf("%d%d",&a,&b);
237                 if(!sam(tree+a,tree+b))printf("-1\n");
238                 else
239                 {
240                     changeroot(tree+a);
241                     access(tree+b);
242                     node *q=getroot(tree+b);
243                     printf("%d\n",q->mm);
244                 }        
245             }
246         }
247         printf("\n");
248     }
249     return 0;
250 }

④习题推荐

我推荐几个LCT的练习题练习下上述的知识点吧!

  • BZOJ 2049 SDOI2008洞穴勘探,模板题,只需要link和cut,然后询问连通性。
  • BZOJ 2002 HNOI2010弹飞绵羊,模板题,需要link和询问某点到根的路径长度。
  • BZOJ 3669 NOI2014魔法森林,LCT的综合应用。

相信做完上述例题,你也能对LCT有了个初步的认识吧,加油!

⑤参考资料

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏恰童鞋骚年

设计模式的征途—8.桥接(Bridge)模式

在现实生活中,我们常常会用到两种或多种类型的笔,比如毛笔和蜡笔。假设我们需要大、中、小三种类型的画笔来绘制12中不同的颜色,如果我们使用蜡笔,需要准备3*12=...

1423
来自专栏腾讯移动品质中心TMQ的专栏

MBT探索系列 - PRE/POST 模型在网络接口测试MBT的应用和探索

目录 ⊙MBT 是什么? ⊙PRE/POST 模型是什么? ⊙如何建立PRE/POST模型? ⊙OCL是什么? ⊙OCL怎么建立PRE/POST模型? ⊙PRE...

3758
来自专栏腾讯AlloyTeam的专栏

教你用 webgl 快速创建一个小世界

Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成一个物体都变得很复杂……这...

1.8K0
来自专栏我的小碗汤

19个很有用的 ElasticSearch 查询语句 篇二

另一个结构化查询的例子是 范围查询。在这个例子中,我们要查找 2015 年出版的书。

1.6K3
来自专栏CSDN技术头条

Hadoop旧mapreduce的map任务切分原理

前言 最近在工作过程中接触一些Hive数据仓库中的表,这些表实际是从关系型数据库通过Sqoop抽到Hive的。在开发过程中对map任务的划分进行性能调优,发现...

21610
来自专栏Python小屋

猜数游戏用Python应该这样写

from random import randint def guess(): #随机生成一个整数 value = randint(1,1000...

3039
来自专栏iOSer成长记录

OpenGL ES(一) 初识

1115
来自专栏青玉伏案

算法与数据结构(六) 迪杰斯特拉算法的最短路径(Swift版)

上篇博客我们详细的介绍了两种经典的最小生成树的算法,本篇博客我们就来详细的讲一下最短路径的经典算法----迪杰斯特拉算法。首先我们先聊一下什么是最短路径,这个还...

2665
来自专栏钱坤的专栏

cache 淘汰算法:LIRS 算法

LIRS 算法是非常优秀的 cache 淘汰算法,被用于 mysql 5.1之后的版本,这篇文章主要来源于对 LIRS 的发表论文的翻译。

2.2K3
来自专栏生信宝典

Pandas使用 (一)

What is pandas Pandas是python中用于处理矩阵样数据的功能强大的包,提供了R中的dataframe和vector的操作,使得我们在使用p...

4909

扫码关注云+社区

领取腾讯云代金券