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);
        }
    }
}
posted @ 2016-07-12 16:55  KatarinaYuan  阅读(162)  评论(0编辑  收藏  举报