edu166 D
限时每日一题day15。第二次自己做出 2000 分的题目。比上次用时更少了,并且感觉这题根本没有2000分。。。
求能取反的区间数量,使得原合法的括号序列经翻转后仍是一个合法括号序列。
不难发现区间内左右括号的数量一定相等。可以将左括号看作1,右括号看作-1。对该数组作前缀和,得到 \(pre\) 数组。
这样左右括号数量相等的区间 \([l, r]\) 等价于 \(pre[l-1] == pre[r]\)。可以考虑枚举右端点,累计合法左端点的数量。
对某个点 \(r\),其作为区间右端点时,左端点 \(l\) 应满足 \(pre[l-1]==pre[r]\)。可以为每个 \(pre[i]\) 开一个存放位置的 \(vector\),这样可快速得到 \(r\) 的对应位置。
进一步想可以发现,\(l\) 的合法性也是单调的。因为在右端点固定的情况下,\(l\) 越偏左,越容易不合法(判断不合法性的依据是出现了某个 \(pre[i] < 0\)。这个比较显然,就不证明了)。因此可以二分左端点。难点在于如何弄 \(check\) 函数。
对于翻转区间 \([l,r]\),首先一定不会影响 \(i < l\) 的 \(pre[i]\);其次也不会影响 \(i > r\) 的 \(pre[i]\);因此只需要考虑 \(pre[l到r]\) 会不会因翻转而 \(<0\)。
翻转后的改变为:对于所有 \(i \in [l,r]\),\(pre[i] - pre[l - 1]\) 均会变号。而若要保证 \(pre[l到r]\) 均 \(>=0\),需要有:
所以 \([l, r]\) 翻转后需满足:
\(pre[l-1]\) 为定值,提出来:
线段树维护 \(pre\) 区间最大值即可。这样做到了 \(check\) 复杂度 \(O(logn)\),总复杂度 \(O(nlog^{2}n)\)。
code