【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
GSample Output
4
3
3
4HINT
对于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\n"); 139 else if(bl==1) printf("0\n"); 140 else printf("%d\n",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
【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)相关推荐
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏 括号序列
太神了!!!! 这种方法看来不适合我这种蒟蒻!! 下午还是老老实实用分治把!! #include <cstdio> #include <iostream> #include & ...
- BZOJ1095: [ZJOI2007]捉迷藏 【树上距离-括号序列+线段树】
题解戳这里 #include<cstdio> #include<cctype> #include<algorithm> #define maxn 300005 #d ...
- [ZJOI2007]Hide 捉迷藏
[ZJOI2007]Hide 捉迷藏 小岛的博客 黄学长的博客 NOI08 冬令营论文 <数据结构的提炼与压缩> 这个问题竟然还能用线段树做,拿小本本记下来. 转载于:https://ww ...
- BZOJ1095 ZJOI2007 Hide 捉迷藏
BZOJ1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆 动态点分治 posted on 2016-06-25 11:35 wjyi 阅读( ...) 评论( ...) 编辑 收藏 转载 ...
- codeforces 877E. Danil and a Part-time Job (DFS序列+线段树)
传送门:codeforces 877E 题目大意: 有一颗树,树的每个节点有一盏灯,状态为亮或灭.现在可以进行以下两种操作: 1.pow x,将以 x 为根节点的子树(包括根节点)的所有节点的灯的状态 ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治
这题真是十分难写啊 不管是点分治还是括号序列都有一堆细节.. 点分治:时空复杂度$O(n\log^2n)$,常数巨大 主要就是3个堆的初始状态 C堆:每个节点一个,为子树中的点到它父亲的距离的堆. B ...
- 「BZOJ1095」[ZJOI2007] Hide 捉迷藏
题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条 ...
- [ZJOI2007]Hide 捉迷藏(数据结构)
LCT QTree也行吧. 思路巧妙,速度颠覆人们对LCT的常识. 发现set.find找不到会返回end(),然后你就把他给删了,这会导致在之后的插入时爆炸. 边权下放点权,upd时要小心. 和其他 ...
- 【BZOJ-1095】[ZJOI2007] Hide 捉迷藏【线段树维护树直径】
题意: nnn 个点的一棵树,每个点初始为 000,支持两种操作,第一种操作 CxC \ xC x,表示将第 xxx 个点取反,即 111 变 000,000 变 111.第二种操作为 GGG,表示查 ...
最新文章
- Linux 小知识翻译 - 「cron」
- 三台主机分别部署LAMP
- DIV与Table布局在大型网站的可用性比较
- 《DB2性能管理与实战》导读
- Cpp 对象模型探索 / operator new、operator delete、operator new[] 和 operator delete [] 重载
- 腾讯手游助手android版本,腾讯游戏助手下载-腾讯游戏助手 安卓版v3.3.4.22-PC6安卓网...
- poj 1274 The Perfect Stall
- Java基础---认识正则表达式
- centos gcc 版本安装9.3 c++17
- Windows系统文件浏览标签窗口工具
- 怎么查看ofd格式电子发票? 2种简单方法免费查看
- 【原创】【android逆向系列】1:真机(小米note 3)root(从本人简书博客移入)
- 公历转农历linux C程序
- 计算机硬件组装什,计算机硬件组装的步骤详解
- mac pdf去水印_PDF水印工具for Mac-PDF水印工具Mac版下载 V1.7-PC6苹果网
- Linux基础入门--驱动开发--USB
- 电脑c语言小游戏,C语言中的一个小游戏的排行榜系统...
- 女人不得不学的七个人生规律
- 【视频点播】阿里云视频点播如何获取视频播放的URL
- kobo touch N905B刷Koreader
热门文章
- 总结android项目的基本开发步骤(转帖)
- Tomcat 启动错误 org/eclipse/jdt/debug/core/JDIDebug...
- EMC NetWorker备份oracle安装配置指南
- 水泵怎么做_不是说鱼缸里放置三合一水泵都会起到反作用,也得看什么缸什么鱼...
- Vivado中TCL的使用
- ZYNQ-7000如何生成从Flash和SD卡启动的镜像文件
- 编程珠玑第八章——分治算法求解数组中的最大的连续和
- 世界上最受欢迎的10个Linux发行版
- ES6-Promise对象
- 超强PHP集成环境,支持800多个不同PHP版本同时运行,无限自定义添加mysql与php版本...