• ARC058F


    这是 NOI 班做出的第一道题目,祭一个。

    wdnmd 这是个 Au 题/jk/jk


    Portal

    首先有个很显然的 DP。(dp_{i,j}) 表示考虑到第 (i) 个串,能连接起来的长度为 (j) 的字典序最小的串。那么这样状态数是 (mathrm O(nm)) 的,然后因为要存串,所以时空都至少是 (mathrm O!left(nm^2 ight))。考虑优化之。

    注意到字典序的一个特性(这也是一个常用的套路):它不像其他的值,比如说加法,你前面的加数小一点没关系,后面可能再追回来;而字典序比较过程中,从前往后一旦找到一个不同的字符,那么只需要比较它们即可,这位大的后面再小也永无翻身的机会。

    对于同一个 (i),若对于 (j<k)(dp_{i,j}) 不是 (dp_{i,k}) 的前缀,那么它们已经决出高下,以后后面接什么都没有任何关系了,它们的大小已经固定了;而如果是前缀,则还要看后面接的串表现怎么样。那假如说 (dp_{i,j})(dp_{i,k}) 后面都至少有一种方案接成长度为 (m) 的串,那么可以说 (dp_{i,j})(dp_{i,k}) 中较大的那个卵用已经没有了,之后的 DP 值只要转移到它,则没有任何希望成为答案的前缀。注意这里需要它们有可能补成长度为 (m),也就是后面的串长度们有选择的方案使得和为 (m-j/k)。这个是否有方案只需要从后往前 01 背包 (mathrm O(nm)) 即可。

    我们首先将之后没有机会的 DP 值标记成无效,它们不参与 DP 过程。然后剩下来的都是有机会的,根据上面的结论,所有有卵用的 DP 值从短到长排序后,一定是一个是下一个的前缀这样子。我们认为只有这些有卵用的才是有效的,其他全部标记成无效。那么这样下来,我们可以对于每个 (i) 搞一个母串,每个 DP 值都是这个母串的一个前缀,只需要用一个数表示,并且母串只有一个,这样空间就搞成 (mathrm O(nm)) 了,并且时间也很有希望优化的 Ar 子。

    然后考虑怎么决定出每个 DP 值是否有效,以及母串就是最大的那个有效 DP 值。考虑按 (j) 从小到大 DP,实时维护一个当前所有有效 DP 值的 (j) 的栈,栈的顶端就是当前的母串。(dp_{i,j}) 的决策只有两个,一个是 (dp_{i-1,j}),一个是 (dp_{i-1,j-|a_i|}+a_i),比个大小算出一下 (dp_{i,j})。然后分三种情况:

    1. 当前母串是 (dp_{i,j}) 的前缀。那万事大吉,dark 保持前面那些有效 DP 值不动,将 (dp_{i,j}) 压入栈并令为新母串;
    2. 当前母串小于 (dp_{i,j})。那 (dp_{i,j}) 滚蛋吧,你已经卵用没有了;
    3. 当前母串大于 (dp_{i,j})。那当前母串危,(dp_{i,j}) 要取代你的位置了。于是我们一直弹出,直到栈顶为 (dp_{i,j}) 的前缀,然后实施 (1)。这个栈可以看作一个关于字典序的单调栈(它的确是单调的),于是这个操作的正确性也就不言而喻、一目了然了。

    注意到因为这是个单调栈,还有一些其他线性次数的操作,所以比较字符串的次数一共是 (mathrm O(nm)) 的,每轮 (mathrm O(m)),我们必须要快速比较。每轮分别考虑:显然比较对象只可能是两种形式:上一轮的母串的前缀或上一轮的母串的前缀连上 (a_i)。想要比较字典序大小,难点在于找到第一个不同的位置。可以预处理 (a_i) 和母串的前缀哈希值,然后二分,这样是 (mathrm O(nmlog)) 的;然后不难发现,母串的前缀和母串的前缀肯定是前缀关系,没有不相同的位置,所以我们只需要找 (a_i) 与母串的一个后缀、(a_i)(a_i) 的一个后缀的第一个不相同位置,那这不就是 Z 算法所能做的事情吗(爷青回)?复杂度 (mathrm O(nm+sum|a_i|))

    code

    珍爱生命,远离抄袭!
  • 相关阅读:
    构建之法阅读笔记05
    第十一周的学习进度条
    第十周的学习进度条
    第九周的学习进度条
    UI分析之石家庄铁道大学官网
    个人工作总结10
    个人工作总结09
    Lua 笔记16
    Lua 笔记15
    Lua 笔记14
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/arc058f.html
Copyright © 2020-2023  润新知