2021 ICPC 沈阳 G. Encoded Strings II
怎么有人半年后才补题的啊…
对字符串 $s$, 函数 $F_s(c) = chr(G(c, s))$. 其中, $c$ 表示字符串 $s$ 中的字符, $chr(i)$ 表示第 $i+1$ 个小写字母, $G(c, s)$ 表示字符串 $s$ 中最右边的 $c$ 之后不同字母的个数. 给出一个长度为 $n$ 的字符串 $s$, 对于所有非空子序列 $s_0$, 将 $s_0$ 中的字符替换 $s_{0i}$成 $F_{s_0}(s_{0i})$, 得到共 $2^{|s|} - 1$ 个新的字符串. 输出其中字典序最大的字符串.
$1 \le n \le 1000$, $\Sigma = \{a, b, \dots t\}$ ($|\Sigma| = 20$)
不知道从什么地方下手, 尝试先分析答案的性质. 这也是一个普遍的做法.
先做一个非常平凡的转换, 把选择最大的 $F_{s_0}$ 转换为选择 $s_0$ 使得 $F_{s_0}$ 最大. 那么问题就变成了, 我们要选的子序列是什么样子的. 手玩一下, 可以发现, 子序列一定包含了所有字母, 只有这样, $F_{s_0}$ 的第一个字母才是最大的, 才能够让字典序最大.
接下来手玩可以发现, 第一位接着往后, 相同的字母应该是要连续的. 因为如果不连续, 可以把中间更小的删掉, 让相同的往前提, 拼成连续的, 这样字典序会更大.
再接下来的字母, 要么和之前相同, 要么比之前小 $1$. 如果不是小 $1$, 而是小更多, 就说明之后有比他小 $1$ 的字母出现, 那么把他删掉会更好.
于是, 可以归纳证明到, 答案是单调不递增的字典序最大的序列.
这样, 问题就变成了, 选一个子序列 $s_0$, 满足每个字母都选上, 且第一个字母的数量尽量多, 然后第二个字母的数量尽量多…… 以此类推.
选择的字母顺序不能马上确定, 而字母数量只有 $20$, 所以考虑状压 DP. 设 $dp(S)$ 为从前往后, 包含集合 $S$, 且选出来的字母满足上述条件, 最小的边界位置 (因为要给后续的字母更多的可能性, 所以是最小位置). 这样, 区间 $(dp(S - \{x\}), dp(S)]$ 中的所有 $x$ 都被选上(假设 $x$ 是最优决策点). 于是我们需要记录最优决策点, 才知道怎么选. 转移的时候, 希望 $x$ 的数量越多越好. 但是, 如果跑太远, 可能把某一个字母全部删掉了, 这不是想要的结果. 所以再预处理一个类似的 $f(T)$, 表示从后往前, 包含集合 $T$ 的最大边界位置 (因为从后往前, 所以是最大, 给 $x$ 数量更多的可能性). 这个 $f$ 不是和 $dp$ 对称的, 因为它不需要满足条件, 并且这个 $f$ 非常好求.
在转移的过程中, 枚举 $x \in S$, 区间 $(dp(S - \{x\}), f(\bar S))$, 就是可以选 $x$ 的区间 (如果 $dp(S - \{x\})$ 存在, 或者说, 可以用来转移, 这一点很重要). 我们希望选的数量越多越好, 所以需要预处理每个字母在任意区间的数量 $cnt(x, l, r)$, 这里对每个字母求一个前缀和就可以解决. 如果数量相等, 那就选最后一个位置最小的, 因为要给后面选的字母更多可能性. 所以还要预处理一下, 每个字母最后一个不超过 $r$ 的位置, 记为 $pos(x, r)$. 这样, 我们就可以根据 $cnt(x, dp(S - \{x\} + 1, f(\bar S) - 1))$ 为第一关键字, $pos(x, r)$ 为第二关键字, 找到最优决策点, 记录并转移 $dp(S) = pos(x, r)$.
由于我们是由上一个尽量大转移来的, 而我们上述讨论的 dp 过程, 需要非常注意 $dp(S - \{x\})$ 是否可以用来转移. 也就是说, $dp(S - \{x\})$ 在它自己的 dp 过程中, 有没有选最多的 $y$. 如果不是, 那么它用来转移, 可能会导致, 当前 dp 过程求得到最大数量更多, (或者位置更小), 导致错误.
所以在按 $|S|$ 从小到大枚举的过程中, 枚举完了某一个数量, 要记录这个字母选取的最大数量是多少. 然后下一次循环中, 要判断 $dp(S \{x\})$ 选取 $x$ 的数量是否是最多的. 如果不是, 则不能转移.
我写的代码是枚举完一个数量之后, 把不是最大数量的 dp 值设为不存在, 从而避免转移.
最后根据最优决策点随便处理答案即可.
复杂度 $O(|\Sigma|2^{|\Sigma|} + n|\Sigma|)$.
|
|