BZOJ 1095【ZJOI2007】Hide捉迷藏

来源:互联网 发布:什么软件有哥特式字体 编辑:程序博客网 时间:2024/06/05 17:41

题目描述

分析:

(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 1000000000int 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);        }    }}
0 0
原创粉丝点击