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,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关
着灯的,输出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。
题解
如果这道题不修改,显然我们能够想到树形 $DP$ 或者点分来做。
然而对于修改的,显然不能再用静态的做法实现。
这里就使用一种新的思想:动态点分。
它的主要思想就是通过将的重心相连来维护一棵“点分树”,通过“点分树”中各个节点与其儿子间的联系来实现在线修改和查询的目的。
对于这道题:我们需要的就是对于每个重心,找到以这个重心为起点向其子树延伸出的最长链和次长链。
我们可以考虑维护一个堆,来维护断开重心后形成的各个联通块中所有的黑点到重心的距离。
由于最长链和次长链不能来自同一个联通块,我们考虑再维护一个堆来维护以它为重心分割出的各个联通块的保存距离的堆的堆顶。
值得注意的是,重心自己如果是黑点的话是可以连向任意一个子树,所以不妨假设重心自己也是一个独立的联通块,所以一开始第二个堆中需要 $push(0)$ 。
这第二个堆中的最大值和次大值(如果有的话)就是经过这个重心的最长路径。
我们考虑再维护一个堆,来存第二个堆的堆顶,这样的话,第三个堆的堆顶就是询问的答案。
对于修改操作,显然我们要将其被管辖的所有重心都要进行修改,这时我们只要暴力在“分治树”上跳就行了,因为“分治树”的深度不会大于 $log_2 n$ 层的。
点分复杂度是 $log_2 n$ 的,堆的操作也是 $log_2 n$ 的,总复杂度为 $O(n log_2^2 n)$ 。
//It is made by Awson on 2018.1.6
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define RE register
#define lowbit(x) ((x)&(-(x)))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
using namespace std;
const int N = ;
const int M = ;
const int INF = ~0u>>; int n, m, u, v, fa[N+], light[N+];
char ch[];
struct tt {
int to, next;
}edge[(N<<)+];
int path[N+], top;
void add(int u, int v) {
edge[++top].to = v;
edge[top].next = path[u];
path[u] = top;
}
namespace LCA {
int bin[], lim, dep[N+], fa[N+][];
void dfs(int o, int depth, int father) {
fa[o][] = father, dep[o] = depth;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != father) dfs(edge[i].to, depth+, o);
}
int query(int x, int y) {
if (dep[x] < dep[y]) Swap(x, y);
for (int i = lim; i >= ; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = lim; i >= ; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][];
}
int dist(int x, int y) {return dep[x]+dep[y]-(dep[query(x, y)]<<); }
void main() {
lim = log(n)/log(), bin[] = ; for (int i = ; i <= ; i++) bin[i] = bin[i-]<<;
dfs(, , );
for (int t = ; t <= lim; t++) for (int i = ; i <= n; i++) fa[i][t] = fa[fa[i][t-]][t-];
}
}
struct heap {
priority_queue<int>q, p;
void pop() {
while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
q.pop();
}
void erase(int val) {p.push(val); }
int top() {
while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
return q.top();
}
int sec() {
while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
int tmp = q.top(); q.pop();
while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
int val = q.top();
q.push(tmp); return val;
}
int size() {return q.size()-p.size(); }
void push(int val) {q.push(val); }
}q1[N+], q2[N+], q3;
namespace Point_divide {
int size[N+], mx[N+], vis[N+], root, minsize;
void get_size(int o, int fa) {
size[o] = , mx[o] = ;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) {
get_size(edge[i].to, o);
size[o] += size[edge[i].to];
if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to];
}
}
void get_root(int o, int pa, int fa) {
mx[o] = Max(mx[o], size[pa]-size[o]);
if (mx[o] < minsize) minsize = mx[o], root = o;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o);
}
void get_dist(int o, int pa, int fa) {
q1[root].push(LCA::dist(pa, o));
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) get_dist(edge[i].to, pa, o);
}
void work(int o, int pa) {
minsize = INF; get_size(o, ), get_root(o, o, );
fa[root] = pa, vis[root] = ; q2[root].push(), q1[root].push(LCA::dist(pa, root));
for (int i = path[root]; i; i = edge[i].next)
if (!vis[edge[i].to]) get_dist(edge[i].to, pa, root);
q2[pa].push(q1[root].top()); int rt = root;
for (int i = path[root]; i; i = edge[i].next)
if (!vis[edge[i].to]) work(edge[i].to, rt);
if (q2[rt].size() >= ) q3.push(q2[rt].top()+q2[rt].sec());
}
void main() {work(, ); }
} void turnon(int o) {
if (q2[o].size() >= ) q3.erase(q2[o].top()+q2[o].sec());
q2[o].erase();
if (q2[o].size() >= ) q3.push(q2[o].top()+q2[o].sec());
for (int x = o; fa[x]; x = fa[x]) {
if (q2[fa[x]].size() >= ) q3.erase(q2[fa[x]].top()+q2[fa[x]].sec());
q2[fa[x]].erase(q1[x].top());
q1[x].erase(LCA::dist(o, fa[x]));
if (q1[x].size()) q2[fa[x]].push(q1[x].top());
if (q2[fa[x]].size() >= ) q3.push(q2[fa[x]].top()+q2[fa[x]].sec());
}
}
void turnoff(int o) {
if (q2[o].size() >= ) q3.erase(q2[o].top()+q2[o].sec());
q2[o].push();
if (q2[o].size() >= ) q3.push(q2[o].top()+q2[o].sec());
for (int x = o; fa[x]; x = fa[x]) {
if (q2[fa[x]].size() >= ) q3.erase(q2[fa[x]].top()+q2[fa[x]].sec());
if (q1[x].size()) q2[fa[x]].erase(q1[x].top());
q1[x].push(LCA::dist(fa[x], o));
q2[fa[x]].push(q1[x].top());
if (q2[fa[x]].size() >= ) q3.push(q2[fa[x]].top()+q2[fa[x]].sec());
}
}
void work() {
scanf("%d" ,&n);
for (int i = ; i < n; i++) {
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
LCA::main(); Point_divide::main();
int cnt = n; scanf("%d", &m);
while (m--) {
scanf("%s", ch);
if (ch[] == 'G') {
if (cnt <= ) printf("%d\n", cnt-);
else printf("%d\n", q3.top());
}else {
scanf("%d", &u);
if (light[u]) turnoff(u), cnt++;
else turnon(u), cnt--;
light[u] ^= ;
}
}
}
int main() {
work();
return ;
}