1 条题解

  • 0
    @ 2025-8-24 22:55:49

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Alex_Wei
    **

    搬运于2025-08-24 22:55:49,当前版本为作者最后更新于2024-02-29 01:03:35,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P10215 [CTS2024] 字符串游戏

    翻转字符串,每次删去 s[l,r]s[l, r] 并获得其在 s[1,r]s[1, r] 中的出现次数作为分数,记为 occ(l,r)occ(l, r),则有简单 DP:设 fif_i 表示 s[1,i]s[1, i] 的答案,则 fi=maxj=0i1(occ(j+1,i)fj)f_i = \max_{j = 0} ^ {i - 1} (occ(j + 1, i) - f_j)

    容易做到 O(n2)\mathcal{O}(n ^ 2):建出 SAM,转移 fif_i 前加入 s[1,i]s[1, i] 的所有后缀,即从 s[1,i]s[1, i] 对应的结点开始在 link 树上跳父亲,并将经过结点的 occocc 值加 11O(1)\mathcal{O}(1)occ(p,i)occ(p, i)。通过 dfs 序拍平换成 BIT,则修改和查询均为 O(logn)\mathcal{O}(\log n)

    O(n2)\mathcal{O}(n ^ 2) 次转移是无法接受的。

    首先有个很显然的性质,对 q<pq < pocc(q,i)<occ(p,i)occ(q, i) < occ(p, i)。于是如果 fp<fqf_p < f_q,那么 pp 一定更优。因此只需保留 ff 递增的单调栈上的转移点。

    此外还有 occ(p,i)occ(p,i+1)occ(p, i) \geq occ(p, i + 1),也很容易理解,因为 s[p,i+1]s[p, i + 1] 出现的位置一定也有 s[p,i]s[p, i] 出现。这个性质在算法三、四时需要用到。

    充分发挥人类智慧,每次只取单调栈上的前 2020 个和后 2020 个转移,无法通过。思考一下什么时候这样做会寄掉。打表观察发现,当字符串形如若干个 aaa...ab\texttt {aaa...ab} 拼接时,对较大的 ii,若 si=bs_i = \texttt b,则策略为选择一整个 aaaab\texttt {aaa…ab} 循环节,但该决策点在单调栈中间,导致不能正确转移。

    做法一

    继续打出寄掉时单调栈的信息,发现决策点与其下一个位置的 occocc 相差不大,但 ff 值相差非常大,根本不是同一个数量级:决策点的位置是前一个 b\texttt b,且对 si=as_i = \texttt afi=O(i)f_i = \mathcal{O}(i),但对 si=bs_i = \texttt bfi=O(iL)f_i = \mathcal{O}(\frac i {L}),其中 LL 是循环节长度。

    基于打表观察得出的信息,我们再次充分发挥人类智慧,认为对于单调栈上的相邻两个位置 p,qp, q,只有当 fpc<fqf_p \cdot c < f_q 时,pp 才会作为转移点,其中 cc 是一个大于且接近 11 的常数,c=1c = 1 即普通单调栈。经测试,取 c=[1.05,2]c = [1.05, 2] 均可通过,cc 较小时常数过大,cc 较大时不能保证正确性。

    时间复杂度 O(nlog2n)\mathcal{O}(n\log ^ 2n),正确性未知,但是比 std 快。

    做法二

    从出题人角度思考,一定会造出大循环串套小循环串卡掉这种做法。循环串会导致单调栈上一段区间的 ff 形成等差数列,大胆猜测根据某种神秘的单调性,只保留等差数列的两端是正确的,或者说,很难卡掉正确性。但是转移点数量依然为 O(n)\mathcal{O}(n)

    既然只保留一条直线的两端是可行的(指可以通过本题测试数据的正确性验证,并非结论),考虑到斜率会下降,我们不妨更大胆一些,只保留凸包上的点(每个点的横坐标是它在单调栈上的位置,不是在原序列的位置,很幽默)。虽然转移点数量为 O(n)\mathcal{O}(\sqrt n),不仅时间复杂度不对,正确性也没有保证,但它就是通过了,而且比 std 快。

    做法三

    一个不那么明显的观察是,对 q<pq < p,当 ii+1i\to i + 1 时,occ(q,i)occ(q, i) 减小的量不大于 occ(p,i)occ(p, i) 减小的量。因为 s[q,i]s[q, i] 出现的地方 s[p,i]s[p, i] 一定出现,而 s[q,i+1]s[q, i + 1] 相较于 s[q,i]s[q, i] 少出现的地方,s[p,i+1]s[p, i + 1] 也一定相对应地相较于 s[p,i]s[p, i] 少出现了。

    于是一个位置会被它之前的位置反超,这是另类决策单调性,使用二分栈解决,时间复杂度 O(nlog2n)\mathcal{O}(n\log ^ 2n)。需在线计算 occocc,需要可持久化线段树,常数过大导致无法通过。

    做法四

    考虑所有没有被 ii 弹出的单调栈上的位置,设最后一个这样的位置为 pp,则 fp<fif_p < f_i。对 q<pq < p,若 qqii 的决策点,则

    $$f_p \geq occ(q + 1, p) - f_q \geq occ(q + 1, i) - f_q = f_i > f_p $$

    矛盾。这说明每次只需用栈顶位置 pp 更新 fif_i,若 fpfif_p \geq f_i 则弹出,否则退出。

    时间复杂度 O(nlogn)\mathcal{O}(n\log n)。需要倍增定位子串,这也是做法一、二比做法四更快的原因。

    • 1

    信息

    ID
    9853
    时间
    2000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者