题目描述
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 $] ] [ [ + ] ] ] [ [ = ] ] ] [ [$ 当 $b>=c$ 时第二段 ] 就被消完了,两段 $[ $连在一起,例如: 于是就得到了几个十分有用的结论 当 $b 当 $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紧接在一个黑点之后$ 于是我们就可以用左右两半的状态转移到一整段的状态啦 还是考虑$s(x,y),s1(a,b),s2(c,d)$ $(x,y)=b $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。 具体细节可以参见代码,注解比较详细(主要是因为自己照着打了一遍也不太看得懂代码……)
$] ] [ [ [ + ] ] [ [ = ] ] [ [ [$
$rightminus:max(a-b),s'是s的一个前缀且s紧接在一个黑点之后$
$leftplus:max(a+b),s'是s的一个后缀且一个黑点紧接在s之后$
$leftminus:max(b-a),s'是s的一个后缀且一个黑点紧接在s之后$ 1 //minamoto
2 #include