[ZJOI2007]Hide 捉迷藏

Description

  捉迷藏 Jiajia和Wind是一對恩愛的夫妻,而且他們有不少孩子。某天,Jiajia、Wind和孩子們決定在家裏玩
捉迷藏遊戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分佈使得任意兩個屋
子都互相可達。遊戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。在起初的
時候,全部的燈都沒有被打開。每一次,孩子們只會躲藏在沒有開燈的房間中,可是爲了增長刺激性,孩子們會要
求打開某個房間的電燈或者關閉某個房間的電燈。爲了評估某一次遊戲的複雜性,Jiajia但願知道可能的最遠的兩
個孩子的距離(即最遠的兩個關燈房間的距離)。 咱們將以以下形式定義每一種操做: C(hange) i 改變第i個房
間的照明狀態,若原來打開,則關閉;若原來關閉,則打開。 G(ame) 開始一次遊戲,查詢最遠的兩個關燈房間的
距離。node

Input

  第一行包含一個整數N,表示房間的個數,房間將被編號爲1,2,3…N的整數。接下來N-1行每行兩個整數a, b,
表示房間a與房間b之間有一條走廊相連。接下來一行包含一個整數Q,表示操做次數。接着Q行,每行一個操做,如
上文所示。ios

Output

  對於每個操做Game,輸出一個非負整數到hide.out,表示最遠的兩個關燈房間的距離。若只有一個房間是關
着燈的,輸出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。ide

題解:

第一次寫動態點分。大概就是說,將每次求出來的重心建成一棵樹,那麼每修改一個點就要暴力的向上修改每個重心的信息。(這裏及如下的點都是指將重心建成一棵樹以後的點)ui

咱們不妨設關燈的點爲黑點,開燈的點爲白點。spa

這道題的正解是每一個點維護兩個個堆A,B以下:code

  A:記錄點i及點i的子樹中黑點到i的父節點的距離(大根堆)blog

  B:記錄點i的子節點的堆A的堆頂(大根堆)遊戲

  C(答案堆):記錄全部點的堆B的最大值和次大值之和。ip

咱們在建樹的同時用數組記錄一下一個節點的第1個父親,第2個父親......第x個父親。

那麼咱們每修改一個點,就要將它和它的全部父親修改(也就是從它到點分樹根節點的那條鏈)。

答案就是C的堆頂。

那麼對於這道題的空間複雜度,均攤下來是O(nlogn)的,因此不會炸

  1 //Never forget why you start
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstdlib>
  5 #include<cstring>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<queue>
  9 #define inf (1e9)
 10 using namespace std;
 11 int n,m;
 12 struct node{
 13   int next,to;
 14 }edge[200005];
 15 struct Heap{
 16   priority_queue<int>a,b;
 17   void clean(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();}
 18   void push(int x){a.push(x);}
 19   void erase(int x){b.push(x);}
 20   void pop(){clean();a.pop();}
 21   int top(){clean();if(a.size())return a.top();else return -inf;}
 22   int size(){return a.size()-b.size();}
 23   int s_top(){
 24     if(size()<2)return -inf;
 25     clean();
 26     int t=a.top(),ret;a.pop();
 27     clean();
 28     ret=a.top();a.push(t);
 29     return ret;
 30   }
 31 }A[100005],B[100005],C;
 32 int head[100005],size;
 33 void putin(int from,int to){
 34   size++;
 35   edge[size].next=head[from];
 36   edge[size].to=to;
 37   head[from]=size;
 38 }
 39 int root,tot,f[100005],cnt[100005],depth[100005],vis[100005],fa[100005][20],dis[100005][20];
 40 void getroot(int r,int fa){
 41   int i;
 42   cnt[r]=1;f[r]=0;
 43   for(i=head[r];i!=-1;i=edge[i].next){
 44     int y=edge[i].to;
 45     if(y!=fa&&!vis[y]){
 46       getroot(y,r);
 47       cnt[r]+=cnt[y];
 48       f[r]=max(f[r],cnt[y]);
 49     }
 50   }
 51   f[r]=max(f[r],tot-cnt[r]);
 52   if(f[root]>f[r])root=r;
 53 }
 54 void getship(int r,int tmp,int father,int dep){
 55   int i;
 56   for(i=head[r];i!=-1;i=edge[i].next){
 57     int y=edge[i].to;
 58     if(!vis[y]&&y!=father){
 59       fa[y][++depth[y]]=tmp;
 60       dis[y][depth[y]]=dep;
 61       getship(y,tmp,r,dep+1);
 62     }
 63   }
 64 }
 65 void buildtree(int r){
 66   int i;
 67   vis[r]=1;getship(r,r,0,1);int all=tot;
 68   for(i=head[r];i!=-1;i=edge[i].next){
 69     int y=edge[i].to;
 70     if(!vis[y]){
 71       if(cnt[y]>cnt[r])cnt[y]=all-cnt[r];tot=cnt[y];
 72       root=0;getroot(y,r);buildtree(root);
 73     }
 74   }
 75 }
 76 void turn_off(int r){//關掉一盞燈
 77   B[r].push(0);//這盞燈能做爲路徑的起點
 78   if(B[r].size()==2)C.push(B[r].top());//恰好等於2,說明正好有一條邊
 79   for(int i=depth[r];i>1;i--){
 80     int t,pre;
 81     if(!A[fa[r][i]].size()){//若是A爲空,那麼加入的dis[r][i-1]必定是堆頂
 82       A[fa[r][i]].push(dis[r][i-1]);//更新A
 83       pre=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top();//記錄當前B對C的貢獻
 84       B[fa[r][i-1]].push(dis[r][i-1]);//更新B
 85       if(pre>0&&pre==B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top())continue;//若是更新後的B對C的貢獻不變,就continue
 86       if(pre>0&&pre!=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top())C.erase(pre),C.push(B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top());
 87       //若是更新後的B對C的貢獻改變,就更新C
 88       else if(B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top()>0)C.push(B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top());
 89       //若是B沒有對C產生貢獻,在加入了這個點以後有產生了貢獻,就直接將貢獻加進去
 90     }
 91     else{
 92       t=A[fa[r][i]].top();
 93       A[fa[r][i]].push(dis[r][i-1]);
 94       if(t<dis[r][i-1]){
 95     pre=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top();
 96     B[fa[r][i-1]].erase(t);B[fa[r][i-1]].push(dis[r][i-1]);
 97     if(pre>0&&pre!=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top())
 98       C.erase(pre),C.push(B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top());
 99       }
100     }
101   }
102 }
103 void turn_on(int r){
104   B[r].erase(0);
105   if(B[r].size()==1)C.erase(B[r].top());
106   for(int i=depth[r];i>1;i--){
107     int t,pre;
108     A[fa[r][i]].erase(dis[r][i-1]);
109     if(A[fa[r][i]].top()<dis[r][i-1]){
110       pre=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top();
111       B[fa[r][i-1]].erase(dis[r][i-1]);
112       if(A[fa[r][i]].size())B[fa[r][i-1]].push(A[fa[r][i]].top());
113       if(pre>0&&pre!=B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top()){
114     C.erase(pre);
115     if(B[fa[r][i-1]].size()>=2)
116       C.push(B[fa[r][i-1]].top()+B[fa[r][i-1]].s_top());
117       }
118     }
119   }
120 }
121 int now,on[100005];
122 void change(int x){
123   if(!on[x])turn_on(x);
124   else turn_off(x);
125   on[x]^=1;
126   if(on[x])now++;
127   else now--;
128 }
129 int main(){
130   int i,j;
131   scanf("%d",&n);
132   memset(head,-1,sizeof(head));
133   for(i=1;i<n;i++){
134     int from,to;
135     scanf("%d%d",&from,&to);
136     putin(from,to);
137     putin(to,from);
138   }
139   f[0]=inf;tot=n;getroot(1,0);buildtree(root);
140   for(i=1;i<=n;i++)fa[i][++depth[i]]=i,turn_off(i);
141   scanf("%d",&m);
142   char s[5];
143   while(m--){
144     scanf("%s",s);
145     if(s[0]=='G'){
146       if(now==n)printf("-1\n");
147       else printf("%d\n",max(C.top(),0));
148     }
149     else{
150       scanf("%d",&j);
151       change(j);
152     }
153   }
154   return 0;
155 }
相關文章
相關標籤/搜索