bzoj1095
捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。
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
4
3
3
4
对于100%的数据, N ≤100000, M ≤500000。
和qtree4差不多。可以用链分治,点分治或边分治。但要支持动态修改。所以就叫动态树分治。
这里介绍一下动态的点分治。就是堆套堆套堆。(边分就是堆套堆,链分就是堆套线段树)。
我们对于一个重心,将每个子树中的点到该子树的距离都存在一个堆中,记为s1。我们可以将这个堆记录在下一层的分治重心中。然后对于每个重心在记录一个堆,插入所有子树的s1中的top,记为s2。再将s2中的第一大和第二大相加,就是经过该重心的最长链,将它存在一个答案堆中。每次询问只要输出答案堆的堆顶。对于关灯就是插点,开灯就是删点。维护一下每一层的s1和s2还有答案堆就好了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
#define N 100010
struct Heap{
priority_queue<int>A,B;
void Push(int x){A.push(x);}
void Erase(int x){B.push(x);}
void Pop(){while(B.size()&&A.top()==B.top()) A.pop(),B.pop();A.pop();}
int Top(){while(B.size()&&A.top()==B.top()) A.pop(),B.pop();return A.top();}
int S_top(){int tmp=Top(); Pop();int ans=Top(); Push(tmp);return ans;}
int Size(){return A.size()-B.size();}
}s1[N],s2[N],ans;
struct edge{
int x,next;
}e[N*2];
int first[N],tot,dis[N][20],dep[N],size[N],root,f[N],n,m,x,y,cnt;
char op;
bool v[N],b[N];
void add(int x,int y){
e[++tot].x=y;
e[tot].next=first[x];
first[x]=tot;
}
void dfs(int x,int y,int s){
size[x]=1; bool b=true;
for(int i=first[x];i;i=e[i].next)
if(!v[e[i].x]&&e[i].x!=y){
dfs(e[i].x,x,s);
size[x]+=size[e[i].x];
if(size[e[i].x]>s/2) b=false;
}
if(s-size[x]>s/2) b=false;
if(b) root=x;
}
void calc(int x,int y,int d,int id,Heap &s){
s.Push(d);
size[x]=1; dis[x][id]=d;
for(int i=first[x];i;i=e[i].next)
if(e[i].x!=y&&!v[e[i].x]){
calc(e[i].x,x,d+1,id,s);
size[x]+=size[e[i].x];
}
}
int divise(int x,int y,int s){
dfs(x,0,s);
int rt=root; v[rt]=true;
dep[rt]=dep[y]+1; f[rt]=y;
s2[rt].Push(0); v[rt]=true;
for(int i=first[rt];i;i=e[i].next)
if(!v[e[i].x]){
Heap tmp;
calc(e[i].x,0,0,dep[rt]+1,tmp);
int rec=divise(e[i].x,rt,size[e[i].x]);
s1[rec]=tmp;
if(tmp.Size())s2[rt].Push(tmp.Top()+1);
}
if(s2[rt].Size()>1) ans.Push(s2[rt].Top()+s2[rt].S_top());
return rt;
}
void change(int x,int k){
if(s2[x].Size()>1)ans.Erase(s2[x].Top()+s2[x].S_top());
if(k==0) s2[x].Erase(0); else s2[x].Push(0);
if(s2[x].Size()>1)ans.Push(s2[x].Top()+s2[x].S_top());
for(int i=x;f[i];i=f[i]){
if(s2[f[i]].Size()>1) ans.Erase(s2[f[i]].Top()+s2[f[i]].S_top());
if(s1[i].Size())s2[f[i]].Erase(s1[i].Top()+1);
if(k==0)s1[i].Erase(dis[x][dep[i]]); else s1[i].Push(dis[x][dep[i]]);
if(s1[i].Size()) s2[f[i]].Push(s1[i].Top()+1);
if(s2[f[i]].Size()>1) ans.Push(s2[f[i]].Top()+s2[f[i]].S_top());
}
}
char BUF[200001],*buf,*end;
#define getch() (buf==end?fread(BUF,1,200000,stdin),buf=BUF,end=buf+200000,*(buf++):*(buf++))
inline void read(int &x){
static char c;
for(c=getch();c<'0'||c>'9';c=getch());
for(x=0;'0'<=c&&c<='9';c=getch())x=x*10+c-'0';
}
int main(){
read(n);
cnt=n;
for(int i=1;i<n;i++){
read(x); read(y);
add(x,y); add(y,x);
}
divise(1,0,n);
read(m);
for(int i=1;i<=m;i++){
for(op=getch();op!='C'&&op!='G';op=getch());
if(op=='G') {
if(cnt>1) printf("%d\n",ans.Top());
else if(cnt==0) puts("-1");
else puts("0");
}
else {
read(x);
if(b[x]) cnt++,b[x]=false,change(x,1);
else cnt--,b[x]=true,change(x,0);
}
}
return 0;
}