1095: [ZJOI2007]Hide 捉迷藏
Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 1602 Solved: 650
[ Submit][ Status][ Discuss]
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。
论文题,非常恶心!线段树需要维护7个域,
区间有括号是rk,区间左括号数lk,以黑点结束的括号序列的前缀和la,前缀差(是逆的)lb,以黑点开始的括号序列的后缀和ra,后缀差rb,两个黑点之间最大的括号序列的长度。
首先明白]]][[[的含义,]表示往上走,[表示往下走,因为序列长度就是距离了。
然后树中的一个点拆成3个时间戳'[','颜色',']',这个通过dfs得到
然后难度在线段树的区间合并。假设序列]]][[[用(a1,b1)表示,其中a1为]的个数,b1为[的个数,此时需要合并(a1,b1),(a2,b2),如果b1=a2,中间的[[[]]]相消,于是变成(a1,b2),否则必然多出来]或者[与a1或者b2合并。
那么长度变化显然是a1+b2+abs(a2-b1),讨论a2和b1的大小, 于是组合出了四种东西la,lb,ra,rb..这个推导还是有点烦,最好直接看代码,自己推推。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ls id<<1,l,mid
#define rs id<<1|1,mid+1,r
#define Maxn 100010
using namespace std;
inline int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
struct edge{
int to,next;
}p[Maxn<<1];
int tot;
int head[Maxn];
void addedge(int u,int v){
p[tot].to=v;
p[tot].next=head[u];
head[u]=tot++;
}
int dfn[Maxn*3],pos[Maxn*3],c[Maxn*3];
int tmpdfn;
void dfs(int u,int fa){
dfn[++tmpdfn]=-1;
dfn[++tmpdfn]=u;
pos[u]=tmpdfn; //u的时间戳
for(int i=head[u];i!=-1;i=p[i].next){
int v=p[i].to;
if(v==fa) continue;
dfs(v,u);
}
dfn[++tmpdfn]=-2;
}
const int inf=-0x3f3f3f3f;
struct seg{
int lk,rk,la,lb,ra,rb,d;
seg(){}
seg(int _lk,int _rk,int _la,int _lb,int _ra,int _rb,int _d){
lk=_lk,rk=_rk,la=_la,lb=_lb,ra=_ra,rb=_rb,d=_d;
}
seg operator+(seg a){
int LK=lk+max(a.lk-rk,0),RK=a.rk+max(rk-a.lk,0);
int LA=la,LB=lb,RA=a.ra,RB=a.rb; //la前缀和,lb前缀逆差
//a1+b2+abs(b1-a2)
if(a.lb!=inf) LA=max(LA,lk+rk+a.lb);
if(a.la!=inf) LA=max(LA,lk-rk+a.la);
if(ra!=inf) RA=max(RA,ra+a.rk-a.lk);
if(rb!=inf) RA=max(RA,rb+a.rk+a.lk);
//b1+b2-a1-a2
if(a.lb!=inf) LB=max(LB,rk-lk+a.lb);
//a1+a2-b1-b2
if(rb!=inf) RB=max(RB,rb+a.lk-a.rk);
int D=max(d,a.d);
if(ra!=inf&&a.lb!=inf) D=max(D,ra+a.lb);
if(rb!=inf&&a.la!=inf) D=max(D,rb+a.la);
return seg(LK,RK,LA,LB,RA,RB,D);
}
}tr[Maxn*12];
void pushup(int id){
tr[id]=tr[id<<1]+tr[id<<1|1];
}
void set(int id,int l){
tr[id].lk=dfn[l]==-2;
tr[id].rk=dfn[l]==-1;
tr[id].d=inf;
if(dfn[l]>0&&c[l]) tr[id].la=tr[id].lb=tr[id].ra=tr[id].rb=0;
else tr[id].la=tr[id].lb=tr[id].ra=tr[id].rb=inf;
}
void build(int id,int l,int r){
if(l==r){
set(id,l);
return;
}
int mid=l+r>>1;
build(ls);
build(rs);
pushup(id);
}
void update(int id,int l,int r,int a){
if(l==r){
set(id,l);
return;
}
int mid=l+r>>1;
if(a<=mid) update(ls,a);
else update(rs,a);
pushup(id);
}
int main()
{
int n,m,u,v;
n=read();
tot=0;
memset(head,-1,sizeof head);
for(int i=1;i<n;i++){
u=read(),v=read();
addedge(u,v);
addedge(v,u);
}
tmpdfn=0;
dfs(1,0);
for(int i=1;i<=n;i++) c[pos[i]]=1;
build(1,1,tmpdfn);
m=read();
int num=n;
char s[2];
while(m--){
scanf("%s",s);
if(s[0]=='G'){
if(num==0) puts("-1");
else if(num==1) puts("0");
else printf("%d\n",tr[1].d);
}
else{
u=read();
if(c[pos[u]]==1) num--;
else num++;
c[pos[u]]^=1;
update(1,1,tmpdfn,pos[u]);
}
}
return 0;
}