【BZOJ1095】【ZJOI2007】捉迷藏 括号序列+线段树维护

来源:互联网 发布:php自动跳转url 编辑:程序博客网 时间:2024/05/21 10:00

原题链接

1095: [ZJOI2007]Hide 捉迷藏

Time Limit: 40 Sec  Memory Limit: 162 MB
Submit: 2109  Solved: 868
[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

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

Sample Output

4
3
3
4

HINT

对于100%的数据, N ≤100000, M ≤500000。

Source

简洁题意:给一棵树,一开始全都是黑点,需要支持翻转某个点的颜色(黑变白或白变黑),并询问最远的黑点点对的距离。

小岛姐本来是有个blog的,然而他却把博客关惹= =俺来填个坑吧。

括号序列是啥?之前到处问到处被鄙视= =其实就是第一次访问一个点时加一个左括号,把这个点放在这,继续访问他的儿子,最后离开这个点的时候加一个右括号(这不就是dfs序么= =(gjr神犇:“不!dfs序只有进去的那个序”),都用它维护子树来着……之前还一直以为是今年WC营员交流中提到过的欧拉旅游序啥的……)。

括号序列有一个神奇的性质:任意两点间的距离即为括号序列中将它们之间可匹配的括号全部去掉后剩下的括号的数量(这个……大家画画图脑补下会发现是正确的),然后本题可以用线段树来解决。

为了方便,若无特别说明以下内容我们都省略“将可以匹配的括号删除”这句话。

线段树按照括号序列建树。有一件显然的事情,对于括号序列的一段区间,将可匹配的括号全部去掉后,剩下的括号一定是长这样:“))))))((((((((”。对于线段树上每个结点,我们需要维护七个信息:左括号的和右括号的个数(当然是把能匹配的都删掉之后剩下的),所有黑点到左端点的括号数量的最大值(l1),所有黑点到左端点左括号与右括号的数量的差值的最大值(l2),所有黑点到右端点的括号数量的最大值(r1),所有黑点到右端点右括号与左括号的数量的差值的最大值(r2),以及最远的黑点点对的距离。

线段数结点的合并:跟连续最大和略像但还是有差别。两种括号的数量很好维护,将能匹配的减去即可。最远黑点点对,除了两个子区间的最大值,还可能是跨越了这个区间,即左区间的r1+右区间的l2(左区间的左括号数较多) 或是 左区间的r2+右区间的l1(右区间的左括号数较多)(从这里可以看出维护“差值的最大值”的用处)。剩下的信息维护与此类似。

#include<cstdio>#include<iostream>#define maxn 100000#define inf (1<<25)using namespace std;int vis[maxn+10];struct node{    int l1,l2,r1,r2,c1,c2,dis;    void val(int x){        c1=c2=0;        l1=l2=r1=r2=dis=-inf;        if(x==-1)c2=1;        else if(x==-2)c1=1;        else if(vis[x]==1)l1=l2=r1=r2=0;    }    void merge(node &a,node &b){        c1=a.c1+max(0,b.c1-a.c2);        c2=b.c2+max(0,a.c2-b.c1);        dis=max(max(a.dis,b.dis),max(a.r1+b.l2,a.r2+b.l1));        l1=max(a.l1,max(b.l1-a.c2+a.c1,b.l2+a.c2+a.c1));        l2=max(a.l2,b.l2+a.c2-a.c1);        r1=max(b.r1,max(a.r1-b.c1+b.c2,a.r2+b.c1+b.c2));        r2=max(b.r2,a.r2+b.c1-b.c2);    }}s[maxn*3<<2];int num[maxn*3+10],tot;void update(int o,int l,int r,int p){    if(l==r)s[o].val(num[p]);    else{        int m=l+r>>1;        if(p<=m)update(o<<1,l,m,p);        else update(o<<1|1,m+1,r,p);        s[o].merge(s[o<<1],s[o<<1|1]);    }}void build(int o,int l,int r){    if(l==r)s[o].val(num[l]);    else{        int m=l+r>>1;        build(o<<1,l,m);        build(o<<1|1,m+1,r);        s[o].merge(s[o<<1],s[o<<1|1]);    }}struct EDGE{    int u,v,next;}edge[2*maxn+10];int head[maxn+10],pp;void adde(int u,int v){    edge[++pp]=(EDGE){u,v,head[u]};    head[u]=pp;}int pos[maxn+10];void dfs(int u,int fa){    num[++tot]=-1;    pos[num[++tot]=u]=tot;    for(int i=head[u];i;i=edge[i].next){        int v=edge[i].v;        if(v!=fa)dfs(v,u);    }    num[++tot]=-2;}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++)vis[i]=1;    for(int i=1;i<n;i++){        int u,v;        scanf("%d%d",&u,&v);        adde(u,v);        adde(v,u);    }    dfs(1,0);    build(1,1,tot);    int q,cnt=n;    scanf("%d",&q);    char e[2];    while(q--){        scanf("%s",e);        if(e[0]=='G'){            if(cnt==0)puts("-1");            else if(cnt==1)puts("0");            else printf("%d\n",s[1].dis);        }else{            int u;            scanf("%d",&u);            cnt+=vis[u]=-vis[u];            update(1,1,tot,pos[u]);        }    }       return 0;}


1 0
原创粉丝点击