首页 > 代码库 > bzoj 1095: [ZJOI2007]Hide 捉迷藏

bzoj 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。

 

 

思路

2种做法:动态点分治、线段树维护括号序列。

想了好久才明白动态点分治怎么做。

正准备开始码的时候,突然意识到:这个堆到底要维护多少个啊?代码要写死人啊。

所以在阅读了http://blog.csdn.net/qq_30401759/article/details/50879672之后,

被线段树维护括号序列的做法深深震撼,(太妙了啊)。

这里来复述一下该算法。

一棵树,我们熟知的是dfs序,而括号序列与dfs序稍有不同。

每次访问点x时,往序列中加入左括号,再加入点x。结束访问的时候(即x的子树遍历完的时候,添上右括号)

比如下图:

技术分享

这棵树的括号序列是(1(2)(3(4(7))(5)(6)))

括号序列,有一个很神奇的性质,

两个点x,y,它们的距离,就是括号序列中x到y之间把匹配括号去掉之后剩余括号的数量。

比如求原图中4到6的距离,4和6之间的括号序列是())()(,把匹配括号去掉之后是)(,所以4到6的距离是2。

再仔细想想,这个性质貌似也很正常。

把匹配括号去掉之后剩余括号肯定是)))).....)(......(((这样的。)的个数就是x到lca(x,y)的距离,(的个数就是y到lca(x,y)的距离。

对,括号序列,就是这样。

那么回到题目。

一个括号序列,1到n这些数一开始都是黑色,

修改操作:修改某个数的颜色,从黑变成白,或从白变成黑。

要求维护括号序列中任意两个黑色数之间去匹配剩余括号的最大数量。

然后考虑怎么维护(这个还是有点烦的)。

根据***博客所说,线段树每个节点维护7个值:

s1:"("的个数

s2:")"的个数

l1:某黑点到该区间左端点"("个数与")"个数之和最大值(去除匹配括号的)

r1:某黑点到该区间右端点"("个数与")"个数之和最大值

l2:某黑点到该区间左端点"("个数与")"个数之差最大值

r2:某黑点到该区间右端点")"个数与"("个数之差最大值

ans:该区间任意两黑点之间去匹配括号后"("个数与")"个数之和的最大值。

那么,假设当前区间是x,左孩子是l,右孩子是r。

不难得到:
 x.s1=r.s1+max(l.s1-r.s2,0);
 x.s2=l.s2+max(r.s2-l.s1,0);
 x.l1=max(l.l1,max(l.s1+l.s2+r.l2,l.s2+r.l1-l.s1));
 x.r1=max(r.r1,max(r.s1+r.s2+l.r2,r.s1+l.r1-r.s2));
 x.l2=max(l.l2,r.l2+l.s1-l.s2);
 x.r2=max(r.r2,l.r2+r.s2-r.s1);
 x.ans=max(max(l.ans,r.ans),max(l.r1+r.l2,r.l1+l.r2));(这一块比较难理解,不懂可以看上面提到的**博客)

 

代码

  1 #include <cstdio>
  2 #include <iostream>
  3 using namespace std;
  4 #define ref(i,x,y)for(int i=x;i<=y;++i)
  5 #define eef(i,x)for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to)if(y!=f)
  6 int Read()
  7 {
  8     char c=getchar();
  9     for(;c<A||c>Z;c=getchar());
 10     return c;
 11 }
 12 int read()
 13 {
 14     char c=getchar();int d=0,f=1;
 15     for(;c<0||c>9;c=getchar())if(c==-)f=-1;
 16     for(;c>=0&&c<=9;d=d*10+c-48,c=getchar());
 17     return d*f;
 18 }
 19 const int N=100001;
 20 int n,m,cnt,tot;
 21 int head[N],s[N*3],pos[N];
 22 struct xint{int to,next;}e[N*2];
 23 struct node{int x,y,s1,s2,l1,l2,r1,r2,ans;}a[N*3*10];
 24 void add(int x,int y)
 25 {
 26     e[++cnt]=(xint){y,head[x]};head[x]=cnt;
 27     e[++cnt]=(xint){x,head[y]};head[y]=cnt;
 28 }
 29 void pushup(int t)
 30 {
 31     node l=a[t<<1],r=a[t<<1|1];
 32     a[t].s1=r.s1+max(l.s1-r.s2,0);
 33     a[t].s2=l.s2+max(r.s2-l.s1,0);
 34     a[t].l1=max(l.l1,max(l.s1+l.s2+r.l2,l.s2+r.l1-l.s1));
 35     a[t].r1=max(r.r1,max(r.s1+r.s2+l.r2,r.s1+l.r1-r.s2));
 36     a[t].l2=max(l.l2,r.l2+l.s1-l.s2);
 37     a[t].r2=max(r.r2,l.r2+r.s2-r.s1);
 38     a[t].ans=max(max(l.ans,r.ans),max(l.r1+r.l2,r.l1+l.r2));
 39 }
 40 void dfs(int f,int x)
 41 {
 42     s[++tot]=-2;
 43     s[pos[x]=++tot]=x;
 44     eef(i,x)dfs(x,y);
 45     s[++tot]=-1;
 46 }
 47 void build(int x,int y,int t)
 48 {
 49     a[t].x=x;a[t].y=y;
 50     if(x==y)
 51     {
 52         if(s[x]>0)a[t].l1=a[t].l2=a[t].r1=a[t].r2=a[t].ans=0;
 53         else a[t].l1=a[t].l2=a[t].r1=a[t].r2=-1e7,a[t].ans=-1;
 54         if(s[x]==-2)a[t].s1=1;else
 55         if(s[x]==-1)a[t].s2=1;
 56         return;
 57     }
 58     int m=(x+y)>>1;
 59     build(x,m,t<<1);
 60     build(m+1,y,t<<1|1);
 61     pushup(t);
 62 }
 63 void modify(int x,int t)
 64 {
 65     if(a[t].x==a[t].y)
 66     {
 67         if(a[t].l1>-1e7)
 68         {
 69             a[t].l1=a[t].r1=a[t].l2=a[t].r2=-1e7;
 70             a[t].ans=-1;
 71         }else
 72         {
 73             a[t].l1=a[t].r1=a[t].l2=a[t].r2=0;
 74             a[t].ans=0;
 75         }
 76         return;
 77     }
 78     int m=(a[t].x+a[t].y)>>1;
 79     if(x<=m)modify(x,t<<1);
 80     if(x>m)modify(x,t<<1|1);
 81     pushup(t);
 82 }
 83 int main()
 84 {
 85     n=read();
 86     ref(i,2,n)
 87     {
 88         int x=read(),y=read();
 89         add(x,y);
 90     }
 91     dfs(0,1);
 92     build(1,tot,1);
 93     m=read();
 94     while(m--)
 95     {
 96         char c=Read();
 97         if(c==C)modify(pos[read()],1);
 98         else printf("%d\n",a[1].ans);
 99     }
100 }

 

bzoj 1095: [ZJOI2007]Hide 捉迷藏