捉迷藏
Time Limit: 40 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
Description
捉迷藏 Jiajia和Wind是一對恩愛的夫妻,並且他們有很多孩子。某天,Jiajia、Wind和孩子們決定在家里玩捉迷藏游戲。
他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分布使得任意兩個屋子都互相可達。
游戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。
在起初的時候,所有的燈都沒有被打開。
每一次,孩子們只會躲藏在沒有開燈的房間中,但是為了增加刺激性,孩子們會要求打開某個房間的電燈或者關閉某個房間的電燈。
為了評估某一次游戲的復雜性,Jiajia希望知道可能的最遠的兩個孩子的距離(即最遠的兩個關燈房間的距離)。
我們將以如下形式定義每一種操作:
C(hange) i 改變第i房間的照明狀態,若原來打開,則關閉;若原來關閉,則打開。
G(ame) 開始一次游戲,查詢最遠的兩個關燈房間的距離。
Input
第一行包含一個整數N,表示房間的個數,房間將被編號為1,2,3…N的整數。
接下來N-1行每行兩個整數a, b,表示房間a與房間b之間有一條走廊相連。
接下來一行包含一個整數Q,表示操作次數。
接着Q行,每行一個操作,如上文所示。
Output
對於每一個操作Game,輸出一個非負整數,表示最遠的兩個關燈房間的距離。若只有一個房間是關着燈的,輸出0;若所有房間的燈都開着,輸出-1。
Sample Input
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
Sample Output
3
3
4
HINT
對於100%的數據, N ≤100000, M ≤500000。
Main idea
給定一棵樹,有0點或者1點,每次查詢最遠的兩個1點之間的距離,需要支持修改0和1。
Solution
我們先觀察一下數據,由於n<=10^5,所以O(n^2)的做法不可行。我們先考慮如何靜態查詢,首先我們第一反應想到了樹形DP,然后發現這種方法無法優化。考慮一下有什么方法是log級別的呢?我們想到了點分,靜態點分的做法就是每次取出重心,然后查詢最深的1點的深度即可,要如何優化呢?發現如果點分可以動態實現的話就可以AC了。那么現在確定了算法:動態點分治。
我們先從點分的角度來剖析一下,點分其實就相當於每次找到重心,處理和重心有關的路徑,然后把重心割掉,將樹分為多個小塊,這樣將所有路徑上的信息存到了重心上,降低規模處理問題,有效降低復雜度。那么動態點分治就相當於用線段樹處理序列問題一樣,在分治的框架上加上了對於每個點的信息維護,對於每個點用堆來維護信息,這樣來實現信息的維護與查詢。
我們分幾步來實現:
1. 建立“重心樹”:我們發現我們在點分中重心存着路徑的信息,所以我們只要維護跟重心有關的信息就可以了,考慮到分治過程的性質,所以修改了一個點,只會影響到這個點作為一個重心時以上的重心(以下稱為“父重心”),所以我們先根據找重心的過程建立一棵“重心樹”,一個點(作為重心時)隔開后得到的若干棵子樹中的每一個重心的父重心就是這個點(這個點稱為“子重心”),所以可以證明這棵樹的深度是log級別的。每次只需要修改這個點在重心樹中到根的路徑上的點。
2. 構建可刪堆:由於我們要維護的是最長鏈,思考靜態的時候要維護的就是最大值,那么動態時我們就需要一個數據結構來維護這些最大值,支持更改與刪除等操作,我們想到了“堆”,由於這個堆是需要支持刪除操作的,這里討論一下怎么刪除:對於每個heap堆再開一個del堆,刪除一個點的時候將要刪除的值加入到del堆里面,然后調取top的時候如果heap堆和del堆的堆頂是一樣的同時pop掉,直到不一樣的時候的top就是真正的top了,其余操作類似。
3. 維護信息:對於每個點開兩個堆維護信息,第一個c堆維護“這個重心的子樹(包括這個重心)到父重心的距離”(求距離用LCA即可),第二個b堆維護“這個重心隔開后的幾個子樹中的最大深度(也就是子重心c堆的堆頂)”,然后全局開一個A堆維護“每一個b堆的最大值和次大值”,那么顯然答案就是堆A的top。
4. 修改操作:這里討論一下將1點變為0點的操作(0變為1類似),每次修改一個點顯然需要直接影響到c堆,將在重心樹中到根位置的中的點的c堆中刪除掉這個點的值,就會影響到b堆,然后最終影響到A堆。每次修改先刪除掉堆A的top,然后在父重心的b堆中刪除掉這個點的c堆的top,刪除掉這個c堆的top,然后再在父重心的b堆中加入這個點的c堆的top即可,修改的時候再維護一下A堆即可,處理一下細節。
Code
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cmath>
7 #include<queue>
8 using namespace std; 9
10 const int ONE=100005; 11
12 int n,T; 13 int x,y; 14 int next[ONE*2],first[ONE*2],go[ONE*2],tot; 15 int Dep[ONE],Turnoff[ONE],Light; 16 int fat[ONE]; 17 int f[ONE][21]; 18 char ch[10]; 19
20 int get() 21 { 22 int res,Q=1; char c; 23 while( (c=getchar())<48 || c>57) 24 if(c=='-')Q=-1; 25 if(Q) res=c-48; 26 while((c=getchar())>=48 && c<=57) 27 res=res*10+c-48; 28 return res*Q; 29 } 30
31 int Add(int u,int v) 32 { 33 next[++tot]=first[u]; first[u]=tot; go[tot]=v; 34 next[++tot]=first[v]; first[v]=tot; go[tot]=u; 35 } 36
37
38 struct Heap_deal 39 { 40 priority_queue <int> heap,delet; 41
42 void add(int x) {heap.push(x);} 43 void del(int x) {delet.push(x);} 44 void Pop() 45 { 46 while(!delet.empty() && heap.top()==delet.top()) 47 { 48 heap.pop(); 49 delet.pop(); 50 } 51 heap.pop(); 52 } 53
54 int Top() 55 { 56 while(!delet.empty() && heap.top()==delet.top()) 57 { 58 heap.pop(); 59 delet.pop(); 60 } 61 return heap.top(); 62 } 63
64 int SecondTop() 65 { 66 int jilu1=Top(); Pop(); 67 int jilu2=Top(); add(jilu1); 68 return jilu2; 69 } 70
71 int Size() 72 { 73 return heap.size()-delet.size(); 74 } 75 }A,b[ONE],c[ONE]; 76
77 void ADD(Heap_deal &a) 78 { 79 if(a.Size()>=2) 80 { 81 int r1=a.Top(); 82 int r2=a.SecondTop(); 83 A.add( r1+r2 ); 84 } 85 } 86
87 void DEL(Heap_deal &a) 88 { 89 if(a.Size()>=2) 90 { 91 int r1=a.Top(); 92 int r2=a.SecondTop(); 93 A.del( r1+r2 ); 94 } 95 } 96
97 namespace PartLCA 98 { 99 void Deal_first(int u,int father) 100 { 101 Dep[u]=Dep[father]+1; 102 for(int i=0;i<=19;i++) 103 { 104 f[u][i+1]=f[f[u][i]][i]; 105 } 106
107 for(int e=first[u];e;e=next[e]) 108 { 109 int v=go[e]; 110 if(v==father) continue; 111 f[v][0]=u; 112 Deal_first(v,u); 113 } 114 } 115
116 int LCA(int x,int y) 117 { 118 if(Dep[x]<Dep[y]) swap(x,y); 119 for(int i=20;i>=0;i--) 120 { 121 if(Dep[f[x][i]]>=Dep[y]) x=f[x][i]; 122 if(x==y) return x; 123 } 124
125 for(int i=20;i>=0;i--) 126 { 127 if(f[x][i]!=f[y][i]) 128 { 129 x=f[x][i]; 130 y=f[y][i]; 131 } 132 } 133 return f[x][0]; 134 } 135
136 int dist(int x,int y) 137 { 138 return Dep[x]+Dep[y]-2*Dep[LCA(x,y)]; 139 } 140 } 141
142
143 namespace PointF 144 { 145 int Min,center,vis_center[ONE]; 146
147 struct power 148 { 149 int size,maxx; 150 }S[ONE]; 151
152
153 void Getsize(int u,int father) 154 { 155 S[u].size=1; 156 S[u].maxx=0; 157 for(int e=first[u];e;e=next[e]) 158 { 159 int v=go[e]; 160 if(v==father || vis_center[v]) continue; 161 Getsize(v,u); 162 S[u].size+=S[v].size; 163 S[u].maxx=max(S[u].maxx,S[v].size); 164 } 165 } 166
167 void Getcenter(int u,int father,int total) 168 { 169 S[u].maxx=max(S[u].maxx,total-S[u].size); 170 if(S[u].maxx<Min) 171 { 172 Min=S[u].maxx; 173 center=u; 174 } 175
176 for(int e=first[u];e;e=next[e]) 177 { 178 int v=go[e]; 179 if(v==father || vis_center[v]) continue; 180 Getcenter(v,u,total); 181 } 182 } 183
184 void Add_c(int u,int father,int center) 185 { 186 c[center].add(PartLCA::dist(u,fat[center])); 187 for(int e=first[u];e;e=next[e]) 188 { 189 int v=go[e]; 190 if(v==father || vis_center[v]) continue; 191 Add_c(v,u,center); 192 } 193 } 194
195
196 void New_tree(int u,int Last) 197 { 198 Min=n; 199 Getsize(u,0); 200 Getcenter(u,0,S[u].size); 201 vis_center[center]=1; 202
203 fat[center]=Last; 204 if(Last!=0) Add_c(center,0,center); 205 if(c[center].Size()) b[Last].add(c[center].Top()); 206
207 int root=center; 208 for(int e=first[center];e;e=next[e]) 209 { 210 int v=go[e]; 211 if(vis_center[v]) continue; 212 New_tree(v,root); 213 } 214 } 215 } 216
217 namespace Control 218 { 219 void Turn_off(int x) 220 { 221 for(int i=x;fat[i];i=fat[i]) 222 { 223 DEL(b[fat[i]]); 224 if(c[i].Size()) b[fat[i]].del(c[i].Top()); 225
226 c[i].del(PartLCA::dist(fat[i],x)); 227
228 if(c[i].Size()) b[fat[i]].add(c[i].Top()); 229 ADD(b[fat[i]]); 230 } 231 } 232
233 void Turn_on(int x) 234 { 235 for(int i=x;fat[i];i=fat[i]) 236 { 237 DEL(b[fat[i]]); 238 if(c[i].Size()) b[fat[i]].del(c[i].Top()); 239
240 c[i].add(PartLCA::dist(fat[i],x)); 241
242 if(c[i].Size()) b[fat[i]].add(c[i].Top()); 243 ADD(b[fat[i]]); 244 } 245 } 246 } 247
248
249 int main() 250 { 251 n=get(); 252 Light=n; 253 for(int i=1;i<=n;i++) Turnoff[i]=1; 254 for(int i=1;i<n;i++) 255 { 256 x=get(); y=get(); 257 Add(x,y); 258 } 259
260 PartLCA::Deal_first(1,0); 261 PointF::New_tree(1,0); 262
263 for(int i=1;i<=n;i++) ADD(b[i]); 264
265 T=get(); 266 while(T--) 267 { 268 scanf("%s",ch); 269 if(ch[0]=='G') 270 { 271 if(Light==0) printf("-1"); 272 else if(Light==1) printf("0"); 273 else printf("%d",A.Top()); 274 printf("\n"); 275 } 276
277 if(ch[0]=='C') 278 { 279 x=get(); 280 if(Turnoff[x]) 281 { 282 Turnoff[x]=0; 283 Light--; 284 Control::Turn_off(x); 285 } 286 else
287 { 288 Turnoff[x]=1; 289 Light++; 290 Control::Turn_on(x); 291 } 292 } 293 } 294 }

