有点厉害题。对于括号序列和序列上邻项交换的问题的处理有一些启发。
首先考虑如果没有 ox
怎么样。容易发现,我们从前往后记录左括号与右括号的个数差,这个差值一旦为负就立刻从后面提一个右括号过来(一路交换过来),这个做法一定是最优的,并且是唯一最优的操作方法。这样理解比较感性,实际上我们可以对每个分界点计算贡献:如果一个分界点左侧的左括号个数减右括号个数为 \(x < 0\),则一定恰好从右边往左边扔 \(x\) 个左括号,所以在这个分界点进行的交换次数恰好是 \(x\),于是就算出了每个分界点的贡献。代入上面说的策略,会发现每个分界点的交换次数都达到了最优,于是得证。
下面考虑加入 o
和 x
。容易发现如果只有 o
是容易的,考虑每个 o
左侧的左右括号个数就能知道有多少左括号和这个 o
发生交换。然而接下来,当加入 x
时,事情变得棘手起来。由于 x
不能放置在左侧左右括号数目相等的地方,这个限制不能简单转化为某种贡献来计算(x
的位置还会限制它前后的 o
位置,从而改变许多字符被交换的次数)。
首先得出两个结论:首先,在最优解中,把所有左右括号提出来,得到的序列就是它们按照贪心策略得出的最优解。这是因为为了使 x
合法,不会产生额外的 ()
间交换,这种交换直接去掉一定不劣。其次,不会有 ox
间交换。这是因为如果为了让 x
合法而把它往前后移动,这个操作可以等价地改为移动一个前面的一个右括号或后面的一个左括号过来,达到同样效果。
有了这两个结论,我们考虑首先预处理出 ()
构成的序列,计算它经过一系列交换以后形成的序列,每个位置分别来自原序列的哪个位置。接着进行 DP,设 \(F_{i, j}\) 表示第 \(i\) 个 ox
,前面放了 \(j\) 个 ()
,产生的最小贡献。其中一个 o
或 x
的贡献是这样计算的:考虑它前面有多少个元素原本在它后面以及后面有多少元素原本在它前面。由于上述两个结论,这个贡献非常容易通过前后缀和预处理计算,时间复杂度为 \(\mathrm O(n^2)\)。