【BZOJ1095】【ZJOI2007】捉迷藏 [動態點分治]


捉迷藏

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

  8
  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

  4
  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 }
View Code

 


免責聲明!

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



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