数据结构(括号序列,线段树):ZJOI 2007 捉迷藏

【题目描述】

Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。

游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲 藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道 可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。

我们将以如下形式定义每一种操作:

C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。

G(ame)     开始一次游戏,查询最远的两个关灯房间的距离。

【输入格式】

第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。

【输出格式】

对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。

【样例输入】

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

【样例输出】

4

3

3

4

【提示】

对于20%的数据, N ≤50, M ≤100;

对于60%的数据, N ≤3000, M ≤10000;

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

  这道题有三种做法。

  我这里用线段树维护了一个括号序列。

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 using namespace std;
  5 const int maxn=100010;
  6 const int INF=1000000000;
  7 int cnt,fir[maxn],nxt[maxn*2],to[maxn*2];
  8 void addedge(int a,int b){
  9     nxt[++cnt]=fir[a];
 10     fir[a]=cnt;
 11     to[cnt]=b;
 12 }
 13 int c[maxn];
 14 int ID[maxn],rID[maxn*3],tot;
 15 struct Node{
 16     int a,b,l1,l2,r1,r2,dis;
 17     void Init(int p){
 18         dis=-INF;a=b=0;
 19         if(rID[p]==-2)b=1;
 20         if(rID[p]==-3)a=1;
 21         if(rID[p]>0&&c[rID[p]])
 22             l1=l2=r1=r2=0;
 23         else
 24             l1=l2=r1=r2=-INF;
 25     }
 26     void Push_up(Node l,Node r){
 27         int a1=l.a,b1=l.b,a2=r.a,b2=r.b;
 28         if(b1>=a2)a=a1,b=b1+b2-a2;
 29         else a=a1+a2-b1,b=b2;
 30
 31         dis=max(l.dis,r.dis);
 32         dis=max(dis,max(l.r1+r.l2,l.r2+r.l1));
 33
 34         r1=max(r.r1,max(l.r1+b2-a2,l.r2+a2+b2));
 35         r2=max(r.r2,l.r2+a2-b2);
 36
 37         l1=max(l.l1,max(a1-b1+r.l1,a1+b1+r.l2));
 38         l2=max(l.l2,r.l2+b1-a1);
 39     }
 40 }tr[(maxn*3)<<2];
 41
 42 void DFS(int x,int fa){
 43     rID[++tot]=-2;
 44     rID[++tot]=x;
 45     ID[x]=tot;
 46
 47     for(int i=fir[x];i;i=nxt[i])
 48         if(to[i]!=fa)
 49             DFS(to[i],x);
 50     rID[++tot]=-3;
 51 }
 52
 53 void Build(int x,int l,int r){
 54     if(l==r){
 55         tr[x].Init(l);
 56
 57         return;
 58     }
 59     int mid=(l+r)>>1;
 60     Build(x<<1,l,mid);
 61     Build(x<<1|1,mid+1,r);
 62     tr[x].Push_up(tr[x<<1],tr[x<<1|1]);
 63 }
 64
 65 void Modify(int x,int l,int r,int g){
 66     if(l==r){
 67         tr[x].Init(l);
 68         return;
 69     }
 70     int mid=(l+r)>>1;
 71     if(mid>=g)Modify(x<<1,l,mid,g);
 72     else Modify(x<<1|1,mid+1,r,g);
 73     tr[x].Push_up(tr[x<<1],tr[x<<1|1]);
 74 }
 75 int n,Q,x;
 76 char op[10];
 77 int main(){
 78 #ifndef ONLINE_JUDGE
 79     freopen("hide.in","r",stdin);
 80     freopen("hide.out","w",stdout);
 81 #endif
 82     scanf("%d",&n);
 83     for(int i=1;i<=n;i++)c[i]=1;
 84     for(int i=1,a,b;i<n;i++){
 85         scanf("%d%d",&a,&b);
 86         addedge(a,b);
 87         addedge(b,a);
 88     }
 89
 90     DFS(1,1);
 91     Build(1,1,tot);
 92
 93     scanf("%d",&Q);
 94     while(Q--){
 95         scanf("%s",op);
 96         if(op[0]==‘C‘){
 97             scanf("%d",&x);
 98             (c[x])?n--:n++;c[x]^=1;
 99             Modify(1,1,tot,ID[x]);
100         }
101         else{
102             if(n==0)printf("-1\n");
103             else if(n==1)printf("0\n");
104             else printf("%d\n",tr[1].dis);
105         }
106     }
107     return 0;
108 }

  

时间: 2025-01-03 23:54:42

数据结构(括号序列,线段树):ZJOI 2007 捉迷藏的相关文章

CF380C Sereja and Brackets 括号序列+线段树

你可以手画一下,然后发现求的其实就是 $[l,r]$ 区间内合法序列匹配个数. 用线段树维护一下括号序列就可以了. code: #include <bits/stdc++.h> #define N 1000005 #define ll long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin) usin

BZOJ 4034 树上操作(树的欧拉序列+线段树)

刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 注意到操作3,询问x到根的路径之间点权和,容易发现这就是欧拉序列中的前缀和. 所以按照树的欧拉序列建线段树,然后操作1就变成单点修改,操作2,就变成了区间内某些点+a,某些点-a,也容易用tag标记

hdu45221——小明系列问题——小明序列 线段树优化dp

小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 1918    Accepted Submission(s): 583 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找

BZOJ 1798 AHOI2009 Seq 维护序列 线段树

题目大意:维护一个序列,提供三种操作: 1.将区间中每个点的权值乘上一个数 2.将区间中每个点的权值加上一个数 3.求一段区间的和对p取模的值 2631的超^n级弱化版.写2631之前能够拿这个练练手... 线段树区间改动,让学校的大神指导了一下ZKW的区间改动方法,非常好理解,可是代码还是快不了. . . 回头再改改代码吧 可能是我写的太渣了 #include<cstdio> #include<cstring> #include<iostream> #include&

hdu 4521 小明系列问题——小明序列 线段树+二分

小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找去都是自己早已研究过的序列.小明想既然找不到,那就自己来发明一个新的序列问题吧!小明想啊想,终于想出了一个新的序列

【BZOJ4540】【Hnoi2016】序列 线段树

Claris劲啊!CA劲啊! %%%两位线段树做法传送门在这里和这里 逆向题解时间到: 首先将询问按照终点排序,并且一边从到遍历,不妨设当前遍历到了点,对于之前的每个点,我们维护两个值和.(之后的点的两个值都先设成0) 其中表示从这个点到之间序列A的最小值,而,表示从我们遍历第一个点到当前的所有时刻下的各个历史版本的和.(当遍历的点在这个点之前等于零)(事实上.) 不(很)难发现对于每一个询问,当且仅当时,有.因为 也就是说,如果我们把询问全部离线下来,遍历的时候可以快速更新并且求和,,我们就可

hdu 4521 小明系列问题——小明序列(线段树+DP或扩展成经典的LIS)

小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 1553    Accepted Submission(s): 457 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,但是也就由于这样,小明差点儿已经玩遍各种序列问题了.可怜的小明苦苦地在各大站点上寻找着新的序列问题,但是找来

poj 2828 Buy Tickets 【买票插队找位置 输出最后的位置序列+线段树】

题目地址:http://poj.org/problem?id=2828 Sample Input 4 0 77 1 51 1 33 2 69 4 0 20523 1 19243 1 3890 0 31492 Sample Output 77 33 69 51 31492 20523 3890 19243 Hint The figure below shows how the Little Cat found out the final order of people in the queue d

HDU 4521 小明系列问题——小明序列 (线段树维护DP)

题目地址:HDU 4521 基本思路是DP.找前面数的最大值时能够用线段树来维护节省时间. 因为间隔要大于d. 所以能够用一个队列来延迟更新,来保证每次询问到的都是d个之前的. 代码例如以下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include