「BZOJ1095」[ZJOI2007] Hide 捉迷藏
题目描述
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。
题解
神仙般的操作……
膜拜岛娘的思路和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。
具体细节可以参见代码,注解比较详细(主要是因为自己照着打了一遍也不太看得懂代码……)
1 //minamoto 2 #include<bits/stdc++.h> 3 #define N 100005 4 #define inf 0x3f3f3f3f 5 using namespace std; 6 inline int read(){ 7 #define num ch-'0' 8 char ch;bool flag=0;int res; 9 while(!isdigit(ch=getchar())) 10 (ch=='-')&&(flag=true); 11 for(res=num;isdigit(ch=getchar());res=res*10+num); 12 (flag)&&(res=-res); 13 #undef num 14 return res; 15 } 16 int ver[N<<1],Next[N<<1],head[N]; 17 int v[N*3],pos[N],c[N]; 18 int n,q,cnt,tot,black; 19 struct seg{ 20 int l,r,l1,l2,r1,r2,c1,c2,dis; 21 void init(int x){ 22 dis=-inf; 23 c1=c2=0; 24 if(v[x]==-1) c2=1; 25 if(v[x]==-2) c1=1; 26 /*c2为失配左括号,c1为失配右括号 27 为左括号,c2=1;为右括号,c1=1*/ 28 if(v[x]>0&&c[v[x]]) l1=l2=r1=r2=0; 29 else l1=l2=r1=r2=-inf; 30 /*为黑点,l_plus,l_minus,r_plus,r_minus全为0 31 为白点或括号,全为1*/ 32 } 33 }a[N*12]; 34 inline int max(int a,int b,int c){return max(a,max(b,c));} 35 void add(int u,int v){ 36 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; 37 ver[++tot]=u,Next[tot]=head[v],head[v]=tot; 38 } 39 void dfs(int u,int fa){ 40 v[++cnt]=-1; 41 v[++cnt]=u; 42 pos[u]=cnt; 43 for(int i=head[u];i;i=Next[i]) 44 if(ver[i]!=fa) dfs(ver[i],u); 45 v[++cnt]=-2; 46 /*进入加左括号,离开加右括号*/ 47 } 48 inline void merge(seg &s,seg s1,seg s2){ 49 /*r1=max(a+b),r2=max(a-b){s1(a,b)是s前缀且s1紧接在一个黑点之后} 50 l1=max(a+b),l2=max(b-a){s2(a,b)是s后缀且s2紧接在一个黑点之前}*/ 51 int a=s1.c1,b=s1.c2,c=s2.c1,d=s2.c2; 52 s.dis=max(s1.dis,s2.dis); 53 s.dis=max(s.dis,s1.r1+s2.l2,s1.r2+s2.l1); 54 /*s.dis=max(s1.dis,s2.dis,a1+b1-a2+b2,a1-b1+a2+b2)*/ 55 b<c?(s.c1=a-b+c,s.c2=d):(s.c1=a,s.c2=b-c+d); 56 s.r1=max(s2.r1,s1.r1-c+d,s1.r2+c+d); 57 /*a+b=max(a1-b1+a2+b2,a1+b1+b2-a2)*/ 58 s.r2=max(s2.r2,s1.r2+c-d); 59 /*a-b=a1-b1+a2-b2*/ 60 s.l1=max(s1.l1,s2.l1-b+a,s2.l2+b+a); 61 /*同上*/ 62 s.l2=max(s1.l2,s2.l2+b-a); 63 /*b-a=b2-a2+b1-a1*/ 64 } 65 void build(int p,int l,int r){ 66 a[p].l=l,a[p].r=r; 67 if(l==r){ 68 a[p].init(l); 69 return; 70 } 71 int mid=(l+r)>>1; 72 build(p<<1,l,mid); 73 build(p<<1|1,mid+1,r); 74 merge(a[p],a[p<<1],a[p<<1|1]); 75 } 76 void modify(int p,int x){ 77 int l=a[p].l,r=a[p].r; 78 if(l==r){a[p].init(l);return;} 79 int mid=(l+r)>>1; 80 if(x<=mid) modify(p<<1,x); 81 else modify(p<<1|1,x); 82 merge(a[p],a[p<<1],a[p<<1|1]); 83 } 84 int main(){ 85 //freopen("testdata.in","r",stdin); 86 black=n=read(); 87 for(int i=1;i<=n;++i) c[i]=1; 88 for(int i=1;i<n;++i){ 89 int u=read(),v=read(); 90 add(u,v); 91 } 92 dfs(1,0); 93 build(1,1,cnt); 94 q=read(); 95 while(q--){ 96 char s[10]; 97 scanf("%s",s); 98 if(s[0]=='C'){ 99 int x=read(); 100 if(c[x]) --black; 101 else ++black; 102 c[x]^=1; 103 modify(1,pos[x]); 104 } 105 else{ 106 if(!black) puts("-1"); 107 else if(black==1) puts("0"); 108 else printf("%d\n",a[1].dis); 109 } 110 } 111 return 0; 112 }
转载于:https://www.cnblogs.com/bztMinamoto/p/9381655.html
「BZOJ1095」[ZJOI2007] Hide 捉迷藏相关推荐
- 【BZOJ-1095】[ZJOI2007] Hide 捉迷藏【线段树维护树直径】
题意: nnn 个点的一棵树,每个点初始为 000,支持两种操作,第一种操作 CxC \ xC x,表示将第 xxx 个点取反,即 111 变 000,000 变 111.第二种操作为 GGG,表示查 ...
- BZOJ1095 ZJOI2007 Hide 捉迷藏
BZOJ1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆 动态点分治 posted on 2016-06-25 11:35 wjyi 阅读( ...) 评论( ...) 编辑 收藏 转载 ...
- 【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)
1095: [ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游 ...
- [ZJOI2007]Hide 捉迷藏
[ZJOI2007]Hide 捉迷藏 小岛的博客 黄学长的博客 NOI08 冬令营论文 <数据结构的提炼与压缩> 这个问题竟然还能用线段树做,拿小本本记下来. 转载于:https://ww ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏
题目链接 bzoj1095 题意 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且 ...
- BZOJ1095: [ZJOI2007]Hide 捉迷藏(动态点分治)
Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】
题目链接 BZOJ1095 题解 传说中的动态点分治,一直不敢碰 今日一会,感觉其实并不艰涩难懂 考虑没有修改,如果不用树形dp的话,就得点分治 对于每个重心,我们会考虑其分治的子树内所有点到它的距离 ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治
这题真是十分难写啊 不管是点分治还是括号序列都有一堆细节.. 点分治:时空复杂度$O(n\log^2n)$,常数巨大 主要就是3个堆的初始状态 C堆:每个节点一个,为子树中的点到它父亲的距离的堆. B ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习
好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状( ...
最新文章
- FZ35恒流电子负载检测测试
- 一文讲透 “进程、线程、协程”
- mysql 下一条数据_mysql 查询一条记录的下一条和上一条记录
- Vue学习笔记--Vue双向绑定实现原理
- ListView setOnItemClickListener无法响应点击事件解决
- 给大家讲一个被社区团购小程序套路的经历吧
- 【转】JavaScript中的this关键字使用的四种调用模式
- 1731: [Usaco2005 dec]Layout 排队布局*
- 最新emoji表情代码大全_10月最新早上好问候语表情图片大全,朋友们大家早上好表情包!...
- 图书管理系统的分析与设计
- C语言之数组的正向逆向输出
- 系分 02 软件工程
- 计算机信息网络安全保护管理条例,中华人民共和国计算机信息系统安全保护条例...
- Typora安装主题方法
- [].push.apply(a, b)是什么意思
- 普特南数学竞赛的经验
- 分享到QQ空间——网站嵌入分享代码
- IB中文:文学作品中幽默手法的表现方式(上)
- Rose Blumki
- 和导师的微信聊天翻车现场,你一定也经历过!