题目描述

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。

输入输出样例

输入样例#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
输出样例#1:

4
3
3
4

说明

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

对于60%的数据, N ≤3000, M ≤10000; 对于100%的数据, N ≤100000, M ≤500000。

题解

神仙般的操作……

膜拜岛娘的思路和hzwer的代码……

我们先假设有以上这么一棵树(图丑勿介)

进行先序遍历,得到$[A[B[E][F[H][I]]][C][D[G]]]$

再把所有字母去掉$[ [ [ ] [ [ ] [ ] ] ] [ ] [ [ ] ] ]$

这就是这一棵树的括号编码(本质是dfs得到的)

花了这么大功夫找,但这玩意儿到底有什么用呢?

我们考虑两个节点,E和G

取出他们之间的那段括号编码$] [ [ ] [ ] ] ] [ ] [ [$

再将所有匹配的括号去掉,得到$] ] [ [$

我们看到了两个$]$和两个$[$

再回到树上,我们发现E向上走两步,再向下走两步就到达了G

于是发现括号序列可以很方便地维护点与点之间的距离

能不能进一步优化呢?

我们发现,对于距离而言,匹配的括号是没有任何意义的

而且,由于距离只需要记录数字,所以维护括号也是没有意义的,只要有编码就行,可以用一个二元组$(a,b)$来描述它,表示有a个$]$和b个$[$

所以,如果有两个点P和Q,如果介于P和Q之间的括号编码表示为$(a,b)$,则P和Q在树上的距离就是a+b

是不是很方便啊~\(≧▽≦)/~啦啦啦

但是现在问题又来了,怎么维护编码呢?

如果可以通过左边一半的信息和右边一半的信息,从而得到整段编码的信息,就可以用我们熟悉的线段树来维护了

我们可以进行如下的分析

考虑对于两段括号编码$s1(a,b)$和$s2(c,d)$,他们合并起来可以得到$s(x,y)$

注意到$s1$和$s2$合并起来时会产生$min(b,c)$的匹配括号,合并后他们会被抵消掉

于是

当 $b<c$ 时第一段 [ 就被消完了,两段 $]$ 连在一起,例如:

$]  ]  [  [  +  ]  ]  ]  [  [  =  ]  ]  ]  [  [$

当 $b>=c$ 时第二段 ] 就被消完了,两段 $[ $连在一起,例如:
$]  ]  [  [  [  +  ]  ]  [  [  = ] ]  [  [  [$

于是就得到了几个十分有用的结论

当 $b<c$ 时,$(x,y) = (a-b+c,d)$

当 $b>=c$ 时,$(x,y) = (a,b-c+d)$

于是就可以用线段树维护整棵树的括号编码~\(≧▽≦)/~啦啦啦

题目所要求维护的,是$max\{a+b|s'(a,b)是s的一个子串,且s'位于两黑点之间\}$,我们将这个值表示为$dis(s)$

我们先根据上面的两条结论,得到几个推论

$①x+y=a+d+|b-c|=max((a+b-c+d),(a-b+c+d))$

$②x-y=a-b+c-d$

$③y-x=b-a+d-c$

由①式我们可以发现,要维护$dis(s)$,要维护四个值$a+b,d-c,a-b,d+c$

又为了保证$s'$在两个黑点之间,所以要加上一些限制

于是定义出如下四个参数

$rightplus:max(a+b),s'是s的一个前缀且s紧接在一个黑点之后$
$rightminus:max(a-b),s'是s的一个前缀且s紧接在一个黑点之后$
$leftplus:max(a+b),s'是s的一个后缀且一个黑点紧接在s之后$
$leftminus:max(b-a),s'是s的一个后缀且一个黑点紧接在s之后$

于是我们就可以用左右两半的状态转移到一整段的状态啦

还是考虑$s(x,y),s1(a,b),s2(c,d)$

$(x,y)=b<c?(a-b+c,d):(a,b-c+d)$

$dis(s)=max(dis(s1),dis(s2),rightplus(s1)+leftminus(s2),rightminus(s1)+leftplus(s2))$

(把四个参数的值带入上面的等式很容易发现这是正确的)

然后再来考虑如何求出四个参数呢?

$rightplus(s)=max(rightplus(s1)-c+d,rightminus(s1)+c+d,rightplus(s2))$

$rightminus(s)=max(rightminus(s1)+c-d,rightminus(s2))$

$leftplus(s)=max(leftplus(s2)-b+a,left_minus(s1)+b+a,leftplus(s1))$

$leftminus(s)=max(leftminus(s2)+b-a,leftminus(s1))$

然后就可以用线段树处理整个括号编码了

实际实现的时候还有一些小细节要注意

我们为了实现更方便,最好还是在编码时加入括号

对于底层结点,如果对应字符是一个括号或者一个白点,那 么right_plus、right_minus、left_plus、left_minus、dis 的值就都是 -inf;如果对应字符是一个黑点,那么 right_plus、right_minus、left_plus、left_minus 都是 0,dis 是-inf。

具体细节可以参见代码,注解比较详细(主要是因为自己照着打了一遍也不太看得懂代码……)

 //minamoto
#include<bits/stdc++.h>
#define N 100005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
int ver[N<<],Next[N<<],head[N];
int v[N*],pos[N],c[N];
int n,q,cnt,tot,black;
struct seg{
int l,r,l1,l2,r1,r2,c1,c2,dis;
void init(int x){
dis=-inf;
c1=c2=;
if(v[x]==-) c2=;
if(v[x]==-) c1=;
/*c2为失配左括号,c1为失配右括号
为左括号,c2=1;为右括号,c1=1*/
if(v[x]>&&c[v[x]]) l1=l2=r1=r2=;
else l1=l2=r1=r2=-inf;
/*为黑点,l_plus,l_minus,r_plus,r_minus全为0
为白点或括号,全为1*/
}
}a[N*];
inline int max(int a,int b,int c){return max(a,max(b,c));}
void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
void dfs(int u,int fa){
v[++cnt]=-;
v[++cnt]=u;
pos[u]=cnt;
for(int i=head[u];i;i=Next[i])
if(ver[i]!=fa) dfs(ver[i],u);
v[++cnt]=-;
/*进入加左括号,离开加右括号*/
}
inline void merge(seg &s,seg s1,seg s2){
/*r1=max(a+b),r2=max(a-b){s1(a,b)是s前缀且s1紧接在一个黑点之后}
l1=max(a+b),l2=max(b-a){s2(a,b)是s后缀且s2紧接在一个黑点之前}*/
int a=s1.c1,b=s1.c2,c=s2.c1,d=s2.c2;
s.dis=max(s1.dis,s2.dis);
s.dis=max(s.dis,s1.r1+s2.l2,s1.r2+s2.l1);
/*s.dis=max(s1.dis,s2.dis,a1+b1-a2+b2,a1-b1+a2+b2)*/
b<c?(s.c1=a-b+c,s.c2=d):(s.c1=a,s.c2=b-c+d);
s.r1=max(s2.r1,s1.r1-c+d,s1.r2+c+d);
/*a+b=max(a1-b1+a2+b2,a1+b1+b2-a2)*/
s.r2=max(s2.r2,s1.r2+c-d);
/*a-b=a1-b1+a2-b2*/
s.l1=max(s1.l1,s2.l1-b+a,s2.l2+b+a);
/*同上*/
s.l2=max(s1.l2,s2.l2+b-a);
/*b-a=b2-a2+b1-a1*/
}
void build(int p,int l,int r){
a[p].l=l,a[p].r=r;
if(l==r){
a[p].init(l);
return;
}
int mid=(l+r)>>;
build(p<<,l,mid);
build(p<<|,mid+,r);
merge(a[p],a[p<<],a[p<<|]);
}
void modify(int p,int x){
int l=a[p].l,r=a[p].r;
if(l==r){a[p].init(l);return;}
int mid=(l+r)>>;
if(x<=mid) modify(p<<,x);
else modify(p<<|,x);
merge(a[p],a[p<<],a[p<<|]);
}
int main(){
//freopen("testdata.in","r",stdin);
black=n=read();
for(int i=;i<=n;++i) c[i]=;
for(int i=;i<n;++i){
int u=read(),v=read();
add(u,v);
}
dfs(,);
build(,,cnt);
q=read();
while(q--){
char s[];
scanf("%s",s);
if(s[]=='C'){
int x=read();
if(c[x]) --black;
else ++black;
c[x]^=;
modify(,pos[x]);
}
else{
if(!black) puts("-1");
else if(black==) puts("");
else printf("%d\n",a[].dis);
}
}
return ;
}

「BZOJ1095」[ZJOI2007] Hide 捉迷藏的更多相关文章

  1. 【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆

    [BZOJ1095][ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉 ...

  2. 【bzoj1095】[ZJOI2007]Hide 捉迷藏 动态点分治+堆

    题目描述 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这 ...

  3. [bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

    [bzoj1095][ZJOI2007]Hide 捉迷藏 2015年4月20日7,8876 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiaji ...

  4. 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏

    简介 这是我自己的一点理解,可能写的不好 点分治都学过吧.. 点分治每次找重心把树重新按重心的深度重建成了一棵新的树,称为分治树 这个树最多有log层... 动态点分治:记录下每个重心的上一层重心,这 ...

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

    1095: [ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏 ...

  6. BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆

    BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子 ...

  7. BZOJ1095:[ZJOI2007]Hide 捉迷藏(动态点分治)

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  8. BZOJ1095: [ZJOI2007]Hide 捉迷藏【线段树维护括号序列】【思维好题】

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  9. BZOJ1095: [ZJOI2007]Hide 捉迷藏【动态点分治】

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

随机推荐

  1. VC2008 类型重定义的问题

    Q: 比如"a.h"里定义了类a,类a所有函数的实现都放在"a.cpp"里.然后"b.h"和"c.h"都需要用到类a,所 ...

  2. 安全运维 -- 更改ssh端口

    环境:Ubuntu 16 前言 黑客遍地都是,ssh/pop3/ftp等爆破工具的流行让站长的日常运维工作量大大加重.Metasplot,Bruter等工具更是针对以上协议有专门 的破解方法,有字典破 ...

  3. yaf框架安装配置

    YAF中文文档:http://www.laruence.com/manual/index.html 1 YAF框架是用C开发的,属于PHP的扩展框架: 2 YAF的性能相对于源生PHP,性能只降低不到 ...

  4. 转:从框架看PHP的五种境界及各自的薪资待遇(仅限于二三线城市,一线除外)

    在撰写此文前首先必须申明的是本人不鄙视任何一种框架,也无意于挑起PHP框架间的战争,更没有贬低某个框架使用者的用意,本文纯粹个人的看法.你可以认为我无知也好,或者装逼也好,请不要试着在任何情况下,随便 ...

  5. 还在手工制作APP规范文档?这款设计神器你不容错过

    之前写了一些关于APP原型文档的文章:一款APP的交互文档从撰写到交付 这次想写下关于APP设计规范文档的内容,规范文档这个东西,实际上大部分中小型公司没有这方面的需求,也没精力去制作这样一个系统性的 ...

  6. 设置zookeeper为systemctl守护进程

    ==目的== 想把zookeeper.hadoop.hbase.storm等大数据组件 设置为开机启动,并且进程挂掉之后,可以自动重启,以减少运维压力. ==service文件== 路径:/usr/l ...

  7. form 表单添加 enctype ="multipart/form-data" 属性后后台接收中文乱码

    解决办法: new String( request.getParameter("title").getBytes("ISO-8859-1"),"utf ...

  8. 向文件写入一个数据块---write

    函数原型:ssize_t write(int fd,const void *buf,size_t count); 参数说明:fd:文件描述符,buf:写入数据的缓冲区,count:写入数据的最大长度. ...

  9. Spring Boot 简单的请求示例(包括请求体验证)

    1.先做个最简单的Get请求 新建一个Controller , 并给他添加注解@RestController 它是@Controller和@ResponseBody的组合注解,告诉Spring我是一个 ...

  10. hdu-1063(大数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1063 思路:1.大数乘法模板 2.考虑小数点的位置 3.乘法前后判断前后道0 参考文章:https:/ ...