【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

 
原文地址:https://www.cnblogs.com/Konjakmoyu/p/6307400.html