列序号括(bracket)
题面在比赛下发文件里。给你一个括号序列,你可以删除 \(i,j\) 当 \(i<j\) 且 \(s_i=\)(
,\(s_j=\))
。问可以形成的字典序最小的括号序列是什么。
首先我们要有一点贪心地考虑什么时候我们才会删除一对括号。如果我们要删除的一对括号中间有东西没有被删除,若中间有 )
,则这个操作是不优的;若中间全部都是 (
,则这个操作可以看做是删除了最右边的那个 (
和 )
。
因此,删除一对括号满足它们中间是空的。所以我们可以一对一对删,每次比较删前和删后的字典序。但是显然这样时间很大,一共会删除 \(O(n)\) 对括号,每次枚举 \(O(n)\),比较 \(O(n)\),一共 \(O(n^3)\)。比较如果用二分哈希优化一下,可以做到 \(o(n^2\log n)\),这里不展开。
我们每次要枚举删除哪对括号,主要是可能有这种情况 ((()()))
,你要删完中间再删左边(位置取左括号位置),我们要把 \(O(n^2)\) 优化成 \(o(n)\),就必须钦定一个删除括号的顺序。因为是字典序,所以我们考虑从后往前删,因为后面的删除会影响前面是否删除。容易发现删除的一定是一个连续的合法的括号序列。设 \(f_i\) 表示考虑到 \(i\) (从后往前)的最小括号序列。转移分 \(i\) 删或者不删转移。如果 \(s_i=\))
显然它左边目前没有可以和它配对的,它只能不删,否则如果不删,\(f_i\gets s_i+f_{i+1}\),如果它要删掉,则以它开头的最短合法括号序列一定会被删掉,记这个最短合法括号序列的右端点为 \(ne_i\),有 \(f_i\gets f_{ne_i+1}\)。
预处理每个位置开头最短合法括号序列,可以从左往右扫,(
当做 \(+1\),)
当做 \(-1\),维护一个永远不小于 \(0\) 的前缀和 \(A\),因为 )
多了是配不了对的,直接清零。然后 \(ne_i\) 就是第一个满足 \(a_j=a_i-1\) 的位置。
DP 状态数是 \(O(n)\) 的了,但是比较两个字符串字典序大小是 \(O(n)\) 的。这里不好哈希,因为你不能每个后缀的答案都维护哈希前缀值,空间就爆了,更新哈希值时间也爆。考虑倍增。每个后缀的答案维护 \(\log\) 个哈希值,转移是 \(O(\log)\) 的。除了哈希的倍增数组,还要维护答案的 \(\log\) 个位置的倍增数组,毕竟无论是更新哈希还是比较字典序,你都要知道跳 \(2^k\) 步之后会到达哪里,这个也是好维护的。
总时间复杂度 \(n\log n\)。
my AC code.写得十分 shit 请见谅