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的基本操作啦。

③例題分析

先來一道簡單的入門題

Time Limit: 851MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu

 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

 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

題意:

給定一棵樹,維護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洞穴勘探,模板題,只需要linkcut,然后詢問連通性。
  • BZOJ 2002 HNOI2010彈飛綿羊,模板題,需要link和詢問某點到根的路徑長度。

  • BZOJ 3669 NOI2014魔法森林,LCT的綜合應用。

相信做完上述例題,你也能對LCT有了個初步的認識吧,加油!

 

⑤參考資料

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM