捉迷藏

Time Limit: 40 Sec Memory Limit: 256 MB

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#include<bits/stdc++.h>
using namespace std;

const int ONE=100005;

int n,T;
int x,y;
int next[ONE*2],first[ONE*2],go[ONE*2],tot;
int Dep[ONE],Turnoff[ONE],Light;
int fat[ONE];
int f[ONE][21];
char ch[10];

int get()
{
int res,Q=1; char c;
while( (c=getchar())<48 || c>57)
if(c=='-')Q=-1;
if(Q) res=c-48;
while((c=getchar())>=48 && c<=57)
res=res*10+c-48;
return res*Q;
}

int Add(int u,int v)
{
next[++tot]=first[u]; first[u]=tot; go[tot]=v;
next[++tot]=first[v]; first[v]=tot; go[tot]=u;
}


struct Heap_deal
{
priority_queue <int> heap,delet;

void add(int x) {heap.push(x);}
void del(int x) {delet.push(x);}
void Pop()
{
while(!delet.empty() && heap.top()==delet.top())
{
heap.pop();
delet.pop();
}
heap.pop();
}

int Top()
{
while(!delet.empty() && heap.top()==delet.top())
{
heap.pop();
delet.pop();
}
return heap.top();
}

int SecondTop()
{
int jilu1=Top(); Pop();
int jilu2=Top(); add(jilu1);
return jilu2;
}

int Size()
{
return heap.size()-delet.size();
}
}A,b[ONE],c[ONE];

void ADD(Heap_deal &a)
{
if(a.Size()>=2)
{
int r1=a.Top();
int r2=a.SecondTop();
A.add( r1+r2 );
}
}

void DEL(Heap_deal &a)
{
if(a.Size()>=2)
{
int r1=a.Top();
int r2=a.SecondTop();
A.del( r1+r2 );
}
}

namespace PartLCA
{
void Deal_first(int u,int father)
{
Dep[u]=Dep[father]+1;
for(int i=0;i<=19;i++)
{
f[u][i+1]=f[f[u][i]][i];
}

for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father) continue;
f[v][0]=u;
Deal_first(v,u);
}
}

int LCA(int x,int y)
{
if(Dep[x]<Dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(Dep[f[x][i]]>=Dep[y]) x=f[x][i];
if(x==y) return x;
}

for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}

int dist(int x,int y)
{
return Dep[x]+Dep[y]-2*Dep[LCA(x,y)];
}
}


namespace PointF
{
int Min,center,vis_center[ONE];

struct power
{
int size,maxx;
}S[ONE];


void Getsize(int u,int father)
{
S[u].size=1;
S[u].maxx=0;
for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father || vis_center[v]) continue;
Getsize(v,u);
S[u].size+=S[v].size;
S[u].maxx=max(S[u].maxx,S[v].size);
}
}

void Getcenter(int u,int father,int total)
{
S[u].maxx=max(S[u].maxx,total-S[u].size);
if(S[u].maxx<Min)
{
Min=S[u].maxx;
center=u;
}

for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father || vis_center[v]) continue;
Getcenter(v,u,total);
}
}

void Add_c(int u,int father,int center)
{
c[center].add(PartLCA::dist(u,fat[center]));
for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father || vis_center[v]) continue;
Add_c(v,u,center);
}
}


void New_tree(int u,int Last)
{
Min=n;
Getsize(u,0);
Getcenter(u,0,S[u].size);
vis_center[center]=1;

fat[center]=Last;
if(Last!=0) Add_c(center,0,center);
if(c[center].Size()) b[Last].add(c[center].Top());

int root=center;
for(int e=first[center];e;e=next[e])
{
int v=go[e];
if(vis_center[v]) continue;
New_tree(v,root);
}
}
}

namespace Control
{
void Turn_off(int x)
{
for(int i=x;fat[i];i=fat[i])
{
DEL(b[fat[i]]);
if(c[i].Size()) b[fat[i]].del(c[i].Top());

c[i].del(PartLCA::dist(fat[i],x));

if(c[i].Size()) b[fat[i]].add(c[i].Top());
ADD(b[fat[i]]);
}
}

void Turn_on(int x)
{
for(int i=x;fat[i];i=fat[i])
{
DEL(b[fat[i]]);
if(c[i].Size()) b[fat[i]].del(c[i].Top());

c[i].add(PartLCA::dist(fat[i],x));

if(c[i].Size()) b[fat[i]].add(c[i].Top());
ADD(b[fat[i]]);
}
}
}


int main()
{
n=get();
Light=n;
for(int i=1;i<=n;i++) Turnoff[i]=1;
for(int i=1;i<n;i++)
{
x=get(); y=get();
Add(x,y);
}

PartLCA::Deal_first(1,0);
PointF::New_tree(1,0);

for(int i=1;i<=n;i++) ADD(b[i]);

T=get();
while(T--)
{
scanf("%s",ch);
if(ch[0]=='G')
{
if(Light==0) printf("-1");
else if(Light==1) printf("0");
else printf("%d",A.Top());
printf("\n");
}

if(ch[0]=='C')
{
x=get();
if(Turnoff[x])
{
Turnoff[x]=0;
Light--;
Control::Turn_off(x);
}
else
{
Turnoff[x]=1;
Light++;
Control::Turn_on(x);
}
}
}
}