【题解】Solution Set - NOIP2024集训Day2 线段树
【题解】Solution Set - NOIP2024集训Day2 线段树
https://www.becoder.com.cn/contest/5431
「CF1149C」Tree Generator™
结论:
对于括号序列的一个子段,删去所有的匹配括号之后,剩下的不匹配的括号,按顺序构成树上的一条路径。
Why?
从括号序列的构造出发。每次
(
相当于开始遍历一个点,)
相当于结束遍历一个点。删掉之后的括号序列,一定是形如
...)))((((...
的。相当于是先从一个点开始向上跳,然后一堆匹配的括号,相当于是跳过这整个子树,然后再不断向下跳。
注意到这个剩下的括号序列在树上是有顺序的。
所以其实这个路径就是就是从一开始的不匹配括号到末尾的的不匹配括号之间的路径。
推论1:
一棵树的直径即为其括号序列的任意子段的不匹配括号数量的最大值。
Why?
跟上面思路类似,可以证明树上的任意一条路径都可以被括号序列的一段子段表示。
嗯~面这是非常标准的错误做法,因为我们并不能直接用 d[ls].rans + d[rs].lans
来更新 d[p].ans
,因为中间会抵消一部分,让这个解变的更劣。
然后,就可以正式开始这道题了。
线段树上维护:(下面的这些值都应该是二元分别表示一开始 )
的数量和末尾 (
的数量。
- 当前区间内,选取一个子区间,再删去所有的匹配括号之后,剩下的不匹配的括号的数量的最大值 \(ans\);
- 选取的子区间左端点必须是整个区间的左端点 \(lans\);
- 选取的子区间右端点必须是整个区间的右端点 \(rans\);
- 选取整个区间的答案 \(all\);
直接用括号做好像不太好做(几乎可以断言不可做,因为根本没办法合并),所以我们要用一些性质转化合并的东西。
还是括号序列老套路:将 (
赋值为 \(1\),)
赋值为 -1
。
对于一个区间 \([l,r]\):
设 \(sum(l,r)\) 表示区间和。
因为我们已经知道最后答案的形式一定是:...)))(((...
,所以我们不妨考虑枚举一下中间的断点 \(k\)。
那么对于每一个断点 \(k\) 的答案就是:\(-sum(l,k)+sum(k+1,r)=sum(l,r)-2sum(l,k)\)。
所以我们对于每个区间应该维护其每个子区间 \(sum(l,r)-2sum(l,k)\) 的最大值。
注意题解 \(mx2\) 应该 \(=sum(y,r)-sum(x,y)\)
关于实现(push_up
里面更新都遵从:不跨过 mid;断点在 mid 左边;断点在 mid 右边。
「CF212D」Cutting a Fence
直接拿出式子:
考虑每个 \(a_i\) 对每一个 \(ans_k\) 的贡献。
先单调栈找到每个 \(a_i\) 作为最小的极长子区间 \([l,r]\)。
然后对 \(ans_k(k\le r-l+1)\) 贡献就应该是(能贡献的位置的最右边的左端点位置 - 最左边左端点位置):\(\min\{r-k+1,i\}-\max\{l,i-k+1\}+1\)。
然后又是经典套路:二分 \(\min,\max\) 取前者/后者的断点,然后分别分成两段然后差分就好了。(哇塞,被我秒了((👍
实际上她是一次函数啊,直接解不等式就好了,不需要二分。😅
具体的,\(k>r+1-i\) 时,\(\min\) 取左边。\(k<i+1-l\) 时,\(\max\) 取右边。
注意一下重复元素的处理。
md,太兴奋了,打错了一个地方,看半天才看出来。😭
「SHOI2016」随机序列
考虑一个 dp。
设 \(f_{i,0/1}\),前 \(i\) 个,第 \(i\) 前面为 $\pm/\times $ 的和;
上面这个 dp 挂了,但是我不知道为什么。😥
因为加法减法的出现是完全等价的,所以大多数时候在算贡献的时候就都会抵消掉,这就给我们带来了便利。
具体而言肯定只有开头的连续乘法有贡献,那么这道题就变成了计数题(其实也根本算不上。
然后就线段树维护一下区间乘积和答案,就好了。
「CF526F」Pudding Monsters
原题。
将二维问题一维化,还是挺重要的一个技巧。