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
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。
第一次写动态点分治OwO 其实就是把每次找到的重心连成一棵树,这样每改变一个点的状态只要更新它在新树中的位置到根的一条链就行啦。
维护3种堆,C(n个)堆里放每个点的子树中所有点到它的父重心(即在新树中的父亲)的距离。B(n个)堆里放每个点与它的每个子树中的最大距离,即在它的子重心(即在新树中的儿子)的C堆中取MAX。A(1个)堆放每个点的答案,即所有B堆的最大值和次大值之和。
这样就好啦。啊还有要写可删堆,就是用2个堆,另一个存已经删了的元素。
不过我的程序好像比别人要慢很多,要8s+QwQ
这道题还有好多别的做法,括号序列+线段树(并不会),还有漆子超的论文《分治算法在树的路径问题中的应用》里写的边分治和树链剖分(把树链剖分理解为基于链的分治)+线段树,都好厉害啊OwO
#include
#define N 100010
#define K 20
using namespace std;
int m,n,fir[N],to[N<<1],nxt[N<<1],tot(1),used[N<<1],size[N],ms[N];
int root,sum,col[N],h[N],g[N<<1][K],times(0),seq[N<<1],pos[N],lg[N<<1],rt,f[N];
char z[K];
struct heap{
priority_queue<int> a,b;
void push(int x){
a.push(x);
}
void erase(int x){
b.push(x);
}
int top(){
while (!a.empty() && !b.empty() && a.top()==b.top())
a.pop(),b.pop();
return (a.empty()?-1:a.top());
}
int size(){
return a.size()-b.size();
}
int sum(){
if (size()<2)
return -1;
int s=0,t;
t=s=top(); a.pop();
s+=top(); a.push(t);
return s;
}
}A,B[N],C[N];
template
inline void read(Aqua &s){
s=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) s=s*10+c-'0',c=getchar();
}
inline void add(int u,int v){
to[++tot]=v; nxt[tot]=fir[u]; fir[u]=tot;
}
void getrt(int x,int fa){
size[x]=1; ms[x]=0;
for (int i=fir[x];i;i=nxt[i])
if (!used[i] && to[i]!=fa){
getrt(to[i],x);
ms[x]=max(ms[x],size[to[i]]);
size[x]+=size[to[i]];
}
if (ms[x]*2<=sum && size[x]*2>=sum)
root=x;
}
void dfs(int x,int fa){
size[x]=1;
for (int i=fir[x];i;i=nxt[i])
if (!used[i] && to[i]!=fa){
dfs(to[i],x);
size[x]+=size[to[i]];
}
}
void solve(int x){
dfs(x,0);
for (int i=fir[x];i;i=nxt[i])
if (!used[i]){
used[i]=used[i^1]=1;
sum=size[to[i]]; getrt(to[i],0);
f[root]=x; solve(root);
used[i]=used[i^1]=0;
}
}
void dfs_(int x,int fa){
h[x]=h[fa]+1;
seq[++times]=x; pos[x]=times;
for (int i=fir[x];i;i=nxt[i])
if (to[i]!=fa){
dfs_(to[i],x);
seq[++times]=x;
}
}
inline int Min(int a,int b){
return (h[a]1,0); rt=root;
solve(root);
dfs_(1,0);
for (int i=1;i<=times;i++)
g[i][0]=seq[i];
for (int i=1;ifor (int j=1;j+(1<1<=times;j++)
g[j][i]=Min(g[j][i-1],g[j+(1<1)][i-1]);
lg[1]=0;
for (int i=2;i<=times;i++)
lg[i]=lg[i>>1]+1;
}
inline int dis(int a,int b){
if (pos[a]>pos[b]) swap(a,b);
int k=lg[pos[b]-pos[a]+1];
return h[a]+h[b]-2*h[Min(g[pos[a]][k],g[pos[b]-(1<1][k])];
}
void on(int x){
int fa,tmp,d,pas,now;
B[x].erase(0);
if (B[x].size()==1)
A.erase(B[x].top());
for (int cur=x;(fa=f[cur]);cur=fa){
tmp=C[cur].top(); d=dis(fa,x);
C[cur].erase(d); d=C[cur].top();
if (dif (~tmp) B[fa].erase(tmp);
if (~d) B[fa].push(d);
now=B[fa].sum();
if (pas!=now){
if (~pas) A.erase(pas);
if (~now) A.push(now);
}
}
}
}
void off(int x){
int fa,tmp,d,pas,now;
B[x].push(0);
if (B[x].size()==2)
A.push(B[x].top());
for (int cur=x;(fa=f[cur]);cur=fa){
tmp=C[cur].top(); d=dis(fa,x);
C[cur].push(d);
if (d>tmp){
pas=B[fa].sum();
if (~tmp) B[fa].erase(tmp);
B[fa].push(d);
now=B[fa].sum();
if (now>pas){
if (~pas) A.erase(pas);
A.push(now);
}
}
}
}
int main(){
read(n);
int u,v;
for (int i=1;iread(u),read(v);
add(u,v),add(v,u);
}
pre();
int cnt=n;
for (int i=1;i<=n;i++)
off(i);
read(m);
for (int i=1;i<=m;i++){
scanf("%s",z);
if (z[0]=='G'){
if (cnt>1)
printf("%d\n",A.top());
else
printf("%d\n",cnt-1);
}
else{
read(u); col[u]^=1;
if (col[u])
on(u),cnt--;
else
off(u),cnt++;
}
}
return 0;
}