BZOJ 1095【ZJOI2007】Hide捉迷藏
分析:
(1).一道动态树分治的裸题。
(2).神奇的线段树搞法
Solution:
(1).将树型结构转换成欧拉序列,成为线性结构。对于每个点u,拥有in[u]和out[u]的起始和终止位置,若将in[u]看作左括号”(“,out[u]看作右括号”)”,这个线性结构就可以看做一个括号序列,记为S。
(2).对于两点(u,v)(假设S中u出现在v前面),他们的距离=S[ in[u] , out[v] ]这个括号序列去除掉可匹配的括号,剩下的字符串的长度。
注:最后的字符串都是“)))…..(((“ 的形式,可以将其表示成(a,b),其中a表示“)”的个数,b表示“(”的个数
(3).原问题<=>在S这个线性结构中,找两个黑点之间的最大距离。
用线段树搞,需维护7个信息:
- c1,c2:表示(a,b)
- dist:维护的答案
- Lplus(a1+b1),Lminus(b1-a1)
- Rplus(a2+b2),Rminus(a2-b2)
在合并区间(a1,b1),(a2,b2)的时候,
min(b1,a2)可以匹配,需要减去。
<1>.对于a,b
if b1< a2 (a,b)=(a1+a2-b1,b2)
else (a,b)=(a1,b2+b1-a2)
可以写成:
a=a1+max(0,a2-b1)
b=b1+max(0,b1-a2)
<2>.对于a+b
a+b=max( (a1-b1)+(a2+b2) , (a1+b1)+(b2-a2) )
<3>.对于a-b,b-a
a-b=(a1-b1)+(a2-b2)
b-a=(b1-a1)+(b2-a2)
线段树的维护都是建立上述公式上的,具体的写法见代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100000
#define INF 1000000000
int n,mark[MAXN+10],b[MAXN*3+10],cntb,pos[MAXN+10];
bool vis[MAXN+10];
struct SegmentTree{
int c1,c2,Lp,Lm,Rp,Rm,dist;
//(a,b)
//c1:a
//c2:b
//Lp:Lplus (a1+b1)
//Lm:Lminus (b1-a1)
//Rp:Rplus (a2+b2)
//Rm:Rminus (a2-b2)
//dist:ans
void val(int x){
c1=c2=0;
Lp=Lm=Rp=Rm=dist=-INF;
if(x==-1) c2=1;
else if(x==-2) c1=1;
else if(mark[x]==1) Lp=Lm=Rp=Rm=dist=0;
}
void Update(const SegmentTree &a,const SegmentTree &b){
c1=a.c1+max(0,b.c1-a.c2);
c2=b.c2+max(0,a.c2-b.c1);
//a+b = max( (a1-b1)+(a2+b2) , (a1+b1)+(b2-a2) )
dist=max(max(a.dist,b.dist),max(a.Rp+b.Lm,a.Rm+b.Lp));
Lp=max(a.Lp,max(a.c1+a.c2+b.Lm,a.c1-a.c2+b.Lp));
Rp=max(b.Rp,max(a.Rm+b.c1+b.c2,a.Rp-b.c1+b.c2));
//a-b = (a1-b1) + (a2-b2)
Lm=max(a.Lm,(a.c2-a.c1)+b.Lm);
Rm=max(b.Rm,(b.c1-b.c2)+a.Rm);
}
}tre[(MAXN*3)*4+10];
struct node{
int v;
node *next;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=&edge[0];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF){
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return ;
}
}
}
void addedge(int u,int v){
node *p=++ecnt;
p->v=v;
p->next=adj[u];
adj[u]=p;
}
void dfs(int u)
{
vis[u]=true;
b[++cntb]=-1;
b[++cntb]=u;
pos[u]=cntb;
for(node *p=adj[u];p;p=p->next)
if(!vis[p->v])
dfs(p->v);
b[++cntb]=-2;
}
void read()
{
int x,y;
Read(n);
for(int i=1;i<n;i++){
Read(x),Read(y);
addedge(x,y);
addedge(y,x);
}
dfs(1);
}
void Build(int u,int l,int r)
{
if(l==r){
tre[u].val(b[l]);
return ;
}
int mid=(l+r)>>1;
Build(u<<1,l,mid),Build((u<<1)|1,mid+1,r);
tre[u].Update(tre[u<<1],tre[(u<<1)|1]);
}
void Modify(int u,int l,int r,int pos)
{
if(l==r){
tre[u].val(b[l]);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) Modify(u<<1,l,mid,pos);
else Modify((u<<1)|1,mid+1,r,pos);
tre[u].Update(tre[u<<1],tre[(u<<1)|1]);
}
int main()
{
int Q,x,cnt;
char opt[100];
read();
for(int i=1;i<=n;i++)
mark[i]=1; //mark: (1):关灯 (-1):开灯
cnt=n;
Build(1,1,cntb);
Read(Q);
while(Q--){
scanf("%s",opt);
if(opt[0]=='C'){
Read(x);
cnt+=(mark[x]=-mark[x]);
Modify(1,1,cntb,pos[x]);
}
else{
if(cnt==0) puts("-1");
else if(cnt==1) puts("0");
else printf("%d\n",tre[1].dist);
}
}
}