• 【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)


    1095: [ZJOI2007]Hide 捉迷藏

    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

    【分析】

      此乃神题也。。今天的目标就是AC这题ORZ。。

      跪%%%岛姐:

      首先是括号表示法压压压:

      比如这个图:

      是这样子的:[A[B[E][F[H][I]]][C][D[G]]]

      

    去掉字母后的串:[[[][[][]]][][[]]]
    对于两个点PQ,他们的距离是他们之间括号序列化简之后的括号数(画个图想想就能明白,‘[’表示下去,‘]’表示上去)
    那么下面的操作就能用线段树维护了。


    也就是说,题目只需要动态维护:max{a+b | S’(a, b) 是 S 的一个子串,且 S’ 介于两个黑点之间},
    这里 S 是整棵树的括号编码。我们把这个量记为 dis(s)。

    现在,如果可以通过左边一半的统计信息和右边一半的统计信息,得到整段编码的统计,这道题就可以用熟悉的线段树解决了。

    这需要下面的分析。

    考虑对于两段括号编码 S1(a1, b1) 和 S2(a2, b2),如果它们连接起来形成 S(a, b)。

    注意到 S1、S2 相连时又形成了 min{b, c} 对成对的括号,合并后它们会被抵消掉。(?..这里 b, c 应该分别是指 b1 和 a2。。。

    所以:

    当 a2 < b1 时第一段 [ 就被消完了,两段 ] 连在一起,例如:
    ] ] [ [  +  ] ] ] [ [  =  ] ] ] [ [
    当 a2 >= b1 时第二段 ] 就被消完了,两段 [ 连在一起,例如:
    ] ] [ [ [  +  ] ] [ [  =  ] ] [ [ [ (?..反了?。。。
    

    这样,就得到了一个十分有用的结论:

    当 a2 < b1 时,(a,b) = (a1-b1+a2, b2),
    当 a2 >= b1 时,(a,b) = (a1, b1-a2+b2)。
    

    由此,又得到几个简单的推论:

    (i) a+b = a1+b2+|a2-b1| = max{(a1-b1)+(a2+b2), (a1+b1)+(b2-a2)}
    (ii) a-b = a1-b1+a2-b2
    (iii) b-a = b2-a2+b1-a1
    

    由 (i) 式,可以发现,要维护 dis(s),就必须对子串维护以下四个量:

    right_plus:max{a+b | S’(a,b) 是 S 的一个后缀,且 S’ 紧接在一个黑点之后}
    right_minus:max{a-b | S’(a,b) 是 S 的一个后缀,且 S’ 紧接在一个黑点之后}
    left_plus:max{a+b | S’(a,b) 是 S 的一个前缀,且有一个黑点紧接在 S 之后}
    left_minus:max{b-a | S’(a,b) 是 S 的一个前缀,且有一个黑点紧接在 S 之后}
    

    这样,对于 S = S1 + S2,其中 S1(a, b)、S2(c, d)、S(e, f),就有

    (e, f) = b < c ? (a-b+c, d) : (a, b-c+d)
    dis(S) = max{dis(S1), left_minus(S2)+right_plus(S1), left_plus(S2)+right_minus(S1), dis(S2)}
    

    那么,增加这四个参数是否就够了呢?
    是的,因为:

    right_plus(S) = max{right_plus(S1)-c+d, right_minus(S1)+c+d, right_plus(S2)}
    right_minus(S) = max{right_minus(S1)+c-d, right_minus(S2)}           
    left_plus(S) = max{left_plus(S2)-b+a, left_minus(S2)+b+a, left_plus(S1)}   
    left_minus(S) = max{left_minus(S2)+b-a, left_minus(S1)}               
    

    这样一来,就可以用线段树处理编码串了。实际实现的时候,在编码串中加进结点标号会更方便,对于底层结点,如果对应字符是一个括号或者一个白点,那 么right_plus、right_minus、left_plus、left_minus、dis 的值就都是 -maxlongint;如果对应字符是一个黑点,那么 right_plus、right_minus、left_plus、left_minus 都是 0,dis 是 -maxlongint。

    
    

    现在这个题得到圆满解决,回顾这个过程,可以发现用一个串表达整棵树的信息是关键,这一“压”使得线段树这一强大工具得以利用.. .

    转自:http://www.shuizilong.com/house/archives/bzoj-1095-zjoi2007hide-%E6%8D%89%E8%BF%B7%E8%97%8F/



    猴赛雷啊!!!
    表示即使如此,我还是纠结了好久ORZ。。
    看代码吧、、

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 using namespace std;
      7 #define Maxn 100010
      8 #define INF 0xfffffff
      9 
     10 int mymax(int x,int y) {return x>y?x:y;}
     11 
     12 int a[Maxn];
     13 bool c[Maxn];
     14 int n;
     15 
     16 struct node {int x,y,next;}t[2*Maxn];int len=0;
     17 int first[Maxn];
     18 
     19 void ins(int x,int y)
     20 {
     21     t[++len].x=x;t[len].y=y;
     22     t[len].next=first[x];first[x]=len;
     23 }
     24 
     25 struct nnode
     26 {
     27     int l,r,lc,rc;
     28     int l1,r1,l2,r2,dis,c1,c2;
     29 }tr[2*Maxn];
     30 
     31 int la[Maxn],lb[Maxn],dfn[Maxn];
     32 int na,nb,cnt,tot;
     33 void dfs(int x,int f)
     34 {
     35     nb++;dfn[x]=++tot;
     36     la[++cnt]=na;lb[cnt]=nb;
     37     na=0;nb=0;
     38     for(int i=first[x];i;i=t[i].next) if(t[i].y!=f)
     39     {
     40         int y=t[i].y;
     41         dfs(y,x);
     42     }
     43     na++;
     44 }
     45 
     46 void upd(int x,int y)
     47 {
     48     tr[x].l1=tr[x].l2=tr[x].r1=tr[x].r2=-INF;
     49     tr[x].dis=-INF;
     50     tr[x].c1=la[y+1];tr[x].c2=lb[y+1];
     51     if(c[y+1]==1) tr[x].l1=la[y+1]+lb[y+1],tr[x].l2=lb[y+1]-la[y+1];
     52     if(c[y]==1) tr[x].r1=la[y+1]+lb[y+1],tr[x].r2=la[y+1]-lb[y+1];
     53     if(c[y]&&c[y+1]) tr[x].dis=tr[x].l1;
     54 }
     55 
     56 void merge(int x,int y,int z)
     57 {
     58     tr[z].dis=mymax(tr[x].dis,tr[y].dis);
     59     tr[z].dis=mymax(tr[z].dis,mymax(tr[x].r1+tr[y].l2,tr[x].r2+tr[y].l1));
     60     
     61     int a=tr[x].c1,b=tr[x].c2,c=tr[y].c1,d=tr[y].c2;
     62     
     63     if(b<c) tr[z].c1=a+c-b,tr[z].c2=d;
     64     else tr[z].c1=a,tr[z].c2=b+d-c;
     65     tr[z].l1=mymax(tr[x].l1,mymax(tr[y].l1-b+a,tr[y].l2+a+b));
     66     tr[z].l2=mymax(tr[x].l2,tr[y].l2+b-a);
     67     tr[z].r1=mymax(tr[y].r1,mymax(tr[x].r1+d-c,tr[x].r2+c+d));
     68     tr[z].r2=mymax(tr[y].r2,tr[x].r2+c-d);
     69 }
     70 
     71 int build(int l,int r)
     72 {
     73     int x=++tot;
     74     tr[x].l=l;tr[x].r=r;
     75     if(l==r)
     76     {
     77         upd(x,l);
     78     }
     79     else
     80     {
     81         int mid=(l+r)>>1;
     82         tr[x].lc=build(l,mid);
     83         tr[x].rc=build(mid+1,r);
     84         merge(tr[x].lc,tr[x].rc,x);
     85     }
     86     return x;
     87 }
     88 
     89 void change(int x,int y)
     90 {
     91     if(y<1||y>n) return;
     92     if(tr[x].l==tr[x].r)
     93     {
     94         upd(x,tr[x].l);
     95         return;
     96     }
     97     int mid=(tr[x].l+tr[x].r)>>1;
     98     if(y<=mid) change(tr[x].lc,y);
     99     else change(tr[x].rc,y);
    100     merge(tr[x].lc,tr[x].rc,x);
    101 }
    102 
    103 char s[110];
    104 
    105 int main()
    106 {
    107     int bl;
    108     scanf("%d",&n);
    109     for(int i=1;i<=n;i++) c[i]=1;
    110     bl=n;
    111     memset(first,0,sizeof(first));len=0;
    112     for(int i=1;i<n;i++)
    113     {
    114         int x,y;
    115         scanf("%d%d",&x,&y);
    116         ins(x,y);ins(y,x);
    117     }
    118     na=0,nb=0;cnt=0;tot=0;dfs(1,0); 
    119     n--;
    120     tot=0;build(1,n);
    121     int q;
    122     scanf("%d",&q);
    123     for(int i=1;i<=q;i++)
    124     {
    125         scanf("%s",s);
    126         if(s[0]=='C')
    127         {
    128             int x;
    129             scanf("%d",&x);
    130             if(c[dfn[x]]) bl--;
    131             else bl++;
    132             c[dfn[x]]=c[dfn[x]]?0:1;
    133             change(1,dfn[x]-1);
    134             change(1,dfn[x]);
    135         }
    136         else
    137         {
    138             if(bl==0) printf("-1
    ");
    139             else if(bl==1) printf("0
    ");
    140             else printf("%d
    ",tr[1].dis);
    141         }
    142     }
    143     return 0;
    144 }
    View Code
    
    
    

    2017-01-20 09:11:16

     
  • 相关阅读:
    bzoj 4548 小奇的糖果
    CF1151F Sonya and Informatics
    loj 2392「JOISC 2017 Day 1」烟花棒
    loj 2336「JOI 2017 Final」绳
    luogu P3620 [APIO/CTSC 2007]数据备份
    bzoj 4771 七彩树
    CF765F Souvenirs
    bzoj 3145 [Feyat cup 1.5]Str
    luogu P4482 [BJWC2018]Border 的四种求法
    第五章例题
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6307400.html
Copyright © 2020-2023  润新知