裂人者人恒裂之

137 篇文章 1 订阅
89 篇文章 0 订阅

题目

题目描述
定义只由 () 组成的序列为括号序列。

定义一个括号序列是合法的,当且仅当它的任意一个前缀都满足 ( 的数量不少于 ) 的数量,且整个序列中 () 的数量是相等的。

定义一个括号序列是 “好的”,当且仅当存在两个子序列,满足二者的并集为原序列(但不必不重叠),并且每个子序列都是合法的括号序列。

现在,给定一个长为 n n n 的括号序列。有 m m m 次询问,每次询问给定一个区间 [ L , R ] [L,R] [L,R],你需要求出 [ L , R ] [L,R] [L,R] 中有多少个子区间是 “好的” 括号序列。

数据范围与提示
n , m ≤ 3 × 1 0 5 n,m≤3\times 10^5 n,m3×105 1 ≤ L ≤ R ≤ n 1≤L≤R≤n 1LRn

思路

考虑暴力检查每个子区间。有一个挺不错的贪心思路:为了避免右括号过多,遇到左括号就加入两个子序列,遇到右括号就只加入一个子序列。如果过程中没有左括号不够用的情况,那就只可能有左括号剩余。下文中,都用 a a a 表示左括号数量,用 b b b 表示右括号数量。

最初我认为,只要左括号的剩余不超过 a a a 即可,也就是可以退还一些 ( 。但是 ) 也是可以加的啊!那就不是 ≤ a + b \le a+b a+b 这么简单了,因为此时 ) 的位置同样重要。(我就是被 ())( 轻松的 h a c k \rm hack hack 掉的。)

其实,只要 把原序列翻转(翻转时会让 () 互换)再做一遍就行了。翻转前,保证了右括号不会太多。翻转后,保证了左括号不会过多。——这是感性的证明。理性的证明如下:只要找到一个分界点,使得前缀的 2 a 1 − b 1 2a_1-b_1 2a1b1 等于后缀的 2 b 2 − a 2 2b_2-a_2 2b2a2,就可以前缀按照第一遍的贪心填,后缀按照翻转后的贪心填。

假设它不成立。当 i = 0 i=0 i=0 时,前缀的 2 a 1 − b 1 = 0 2a_1-b_1=0 2a1b1=0 所以 2 b 2 − a 2 > 0 2b_2-a_2>0 2b2a2>0 。同理, i = n i=n i=n 2 a 1 − b 1 > 0 2a_1-b_1>0 2a1b1>0 。故而 2 a 1 − b 1 2a_1-b_1 2a1b1 2 b 2 − a 2 2b_2-a_2 2b2a2 的大小关系有过变化。而二者的差值的变化量是 ± 1 \pm 1 ±1(分类讨论一下当前是 ( 还是 ) 即可知),所以过程中必然相交。相交则有解。

从证明中跳出来——我们已经知道,子区间是 “好的” 的充要条件了,即前缀和 ≥ 0 \ge 0 0 与后缀和 ≥ 0 \ge 0 0 。不妨先看一看前缀和如何维护。

从小到大枚举右端点,线段树维护前缀和的区间最小值,修改时,如果一个区间的最小值 < 0 <0 <0 了,就递归往下来删除它。由于每个点做多被删除一次,总复杂度还是 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 的。

然后考虑后缀和如何维护。这个可以对于每个点预处理 L i L_i Li 为,使得 [ L i , i ] [L_i,i] [Li,i] 的所有后缀和都 ≥ 0 \ge 0 0 的最小 L i L_i Li 。用单调栈预处理——本质是,求前缀和第一个比自己大的。

最后一个问题:怎么统计答案?就是这个问题把我一下子难倒了。我还觉得需要用可持久化的黑科技,将很多线段树给 “重叠” 在一起。其实这个问题还是思维定式,我始终觉得需要在 r r r 这里计算答案。其实,权值可以放到左端点上。线段树上打一个标记,表示 “没有被删除的 l l l 对应的答案加一”,实际含义就是,对于每个 l l l 存储了 ∀ r ∈ [ l , R ] \forall r\in[l,R] r[l,R] 有多少个 “好的” 子区间 [ l , r ] [l,r] [l,r] 。然后询问就是 区间查询 了。

复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn),只有一个线段树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值