复健 week1
ARC223 E - Yin-Yang Two Bits Insertion
手玩发现 \(01 / 10\) 扩张出来是 \(0101 / 1010\),依然是 \(01\) 交错的形式。而 \(00 / 11\) 扩张出来是 \(0110 / 1001\),相当于把相同的对往里推了。于是很直观的想法就是按照 \(00 / 11\) 分段,段段对应。
\(01\) 交错的方式其实并不是很直观(看起来比较乱),一个办法是反转奇数位,这样会发现 \(\text{xy}\) 操作后会变为 \(\text{xxyy}\),于是可以按照相同的值分段,段段对应。(会发现这与考虑交错是等价的。)
段与段对应之后只关心每个段的长度了,记 \(L\) 为段数,\(a_i, b_i\) 分别为 A,B 对应段的长度,手玩可能的操作(为了直观考虑 B 变为 A):
- 选择 \(1\le i < L\) 且 \(b_i, b_{i + 1}\ge 2\) 的 \(i\),让 \(b_i, b_{i + 1}\) 同时减 \(1\)。
- 选择 \(1\le i \le L\) 且 \(b_i\ge 4\) 的 \(i\),让 \(b_i\) 减 \(2\)。
因为操作 2,大部分情况下 \(b_i\) 都能压缩到 \([a_i, a_i + 1]\),唯一特殊的情况是 \(a_i = 1, b_i = 3\)。
又因为操作 1 的存在,可以让相邻的两个段长度各减 \(1\),可以 dp,设计 \(f_{i, j}\) 表示 \((i, i + 1)\) 两段用了 \(j\) 次操作 1 能否合法。
考虑上操作 2 压缩的性质,考虑去掉一些明显不优的 \(j\),能发现 \(j\ge 3\) 都是不优的,因为可以此时每个段的段长 \(\ge j + 1 \ge 4\),所以一定可以变为各用一次操作 2,变成 \(j - 2\) 的情况。需要注意 \(j = 2\) 不能去掉,因为存在 \(a_i = 1, b_i = 3\) 的情况。
于是 \(j\) 这一维只需要保留 \(0\sim 2\),dp 就是线性的了。
时间复杂度 \(\mathcal{O}(n + m)\)。
ARC223 F - Zonal Score Maximization
选择了一个长度为 \(k\) 的子段会导致有 \(k - 2\) 个元素被抛弃了,贪心的就会尽量选择 \(k = 2\),下称长度为 \(k\) 的段为 “\(k\) 段”。
当 \(n\) 为偶数时,用 \(2\) 段可以分割整个序列,此时会取到可能的值的上界 \(n(n + 1)/2\),显然为最大值,所以 \(n\) 为偶数时任意一个排列都能取到该最大值。
当 \(n\) 为奇数时就必须要引入奇数长度的子段了,依然贪心的只会使用 \(3\) 段(\(\ge 4\) 段都可以拆分成两个更小的子段,且原本的 \(\max, \min\) 一定也会被算到,一定劣)。手玩发现使用了 \(\ge 3\) 个 \(3\) 段时一定可以只保留第一个子段,后面全用 \(2\) 段,这说明最优的划分一定只会有一个 \(3\) 段,其余都是 \(2\) 段。需要注意的是 \(3\) 段开头只可能在 \(1, 3, \cdots, n - 2\) 这些位置,称这些 \(3\) 段为合法的 \(3\) 段。
于是一个排列的最大值一定只会是 \(n(n + 1)/2\) 减一个 \([1, n]\) 的被排除掉的值,又因为 \(X\) 已经给定,\(n(n + 1)/2 - X\) 就是这个被排除掉的值,后面称 \(X\) 为这个值。
考虑 \(X\) 怎么样才可能成为被排除的值,因为 \(X\) 是最小的被排除的值,这说明对于任意一个合法的 \(3\) 段其中位数都应 \(\ge X\),且应该存在合法的 \(3\) 段其中位数为 \(X\)。
发现这只涉及大小关系,便可以把值域缩成 \(0, 1, 2\)(对应 \(< X, X, > X\))这 \(3\) 种(需要注意最后从 \(0, 1, 2\) 变回整个排列时内部是需要区分的,所以需要乘上 \(0\) 中为未确定的数量的阶乘,\(2\) 也是同理的),上面的条件变为了:整个序列只有 \(1\) 个 \(1\),任意一个合法的 \(3\) 段 \(0\) 的个数都 \(\le 1\),存在一个合法的 \(3\) 段内部 \(0, 1, 2\) 都恰有 \(1\) 个。
因为序列的每个元素对合法 \(3\) 段贡献的情况有些复杂,直接计数并不太可行。不过考虑到 \(3\) 段天然是有顺序的,可以 dp,记 \(f_{i, j, 0/1/2, 0/1}\) 表示考虑了 \([1, 2i - 1]\) 的序列情况,选出了 \(j\) 个 \(0\),\(2i - 1\) 位置的值是 \(0/1/2\),前面是否出现了 \(0/1\) 的方案数(如果出现了 \(1\) 需要满足存在 \(3\) 段中位数为 \(1\)),接下来决定 \(2i, 2i + 1\) 的值便可递推至 \(f_{i + 1, *, *, *}\)。
进一步发现 \(j\) 这一维是卷积的形式,可以用多项式维护,便可以把每一步 \(f_i\to f_{i + 1}\) 中 \((0/1/2, 0/1)\) 的转移用矩阵形式表示(矩阵内部元素是多项式),然后用分治的方法乘起来,就可以做到 \(\mathcal{O}(6^3 n\log^2 n)\)。
不过矩阵的大小太大了,需要进行缩减。观察到矩阵的大小这么大的原因是引入了 \(= X\) 的 \(1\),就让值域多了一维同时还加入了只能出现 \(1\) 次 \(1\) 的限制。
于是尝试把值域变化转为 \(0, 1\)(分别代表 \(< X, \ge X\)),限制变为每个合法的 \(3\) 段 \(0\) 的个数 \(\le 1\),这样 dp 就只需要记录 \(2i - 1\) 位置是 \(0/1\),矩阵大小就变为了 \(2\)。不过最后得到的方案数是被排除的值 \(\ge X\) 的方案书,因为这是计数题,只需要减掉 \(\ge X + 1\) 的方案数便得到了 \(= X\) 的方案数。
时间复杂度 \(\mathcal{O}(2^3 n\log^2 n)\)。
CF2107 F - Cycling
首先交换一对 \((a_i, a_j)\) 一定是因为马上就要经过 \(i\) 或者 \(j\) 了。
并且贪心的考虑,假设经过的是 \(i\),那么被交换走的 \(a_i\) 一定不会被用到。具体来说,如果 \(j < i\),则如果后面要经过 \(a_i\) 这个值,则不如就在 \(i\) 这个位置直接走过;若 \(j > i\),则一定会是 \(a_j < a_i\),继续往前走 \(a_i\) 这个值不仅离得更远值还更大,一定不优。
于是可以直接抛弃掉这些被交换且无用的值,只考虑会被用到的交换的值,这些值的交换路径的长度和就应该是交换的代价。
其次考虑每个值会被用到的位置,会发现 ABAB 的形态一定是不优的,因为调整成 AABB 后经过的权重和不变但是移动的代价变小了。于是有用的值被用上的位置一定是一个区间 \((l, r)\),这样相当于是把数组切成若干个段 \((l_i, r_i)\),并对每一段选一个贡献的位置 \(p_i\),每一段的总代价就是就是 \(a_{p_i}(r_i - l_i + 1) + |p_i - r_i| + |r_i - l_i|\)。
考虑到如果 \(p_i < r_i\) 则说明 \(p_i\) 在自己的位置都不会是最优的选择,这样还不如让 \(p_i\) 位置选择的值来贡献 \((l_i, r_i)\),所以一定有 \(p_i\le r_i\)。
再考虑如果存在 \(p_i < l_i\) 则考虑前一段 \((l_{i - 1}, r_{i - 1}, p_{i - 1})\),此时一定有 \(a_{p_{i - 1}} \le a_{p_i}\)(如果 \(>\) 肯定全部换成 \(p_i\) 更优)。若 \(a_{p_{i - 1}} < a_{p_i}\),则直接把 \((l_i, r_i)\) 内贡献的值也换为 \(p_{i - 1}\) 即可,此时代价变化是 \(p_i - r_{i - 1} + 1 - (a_{p_i} - a_{p_{i - 1}})(r_i - l_i + 1) \le 0\);若 \(a_{p_{i - 1}} = a_{p_i}\),则把 \(r_{i - 1}, l_i\) 往左移一定也不劣。$所以一定也有 \(p_i\le l_i\)。
这就说明了一定存在最优划分使得 \(l_i\le p_i\le r_i\)。
接下来考虑 dp,但是对于一个段 \((l, r)\) 的贡献还要找对应的 \(p\),于是复杂度就退化到了 \(\mathcal{O}(n^3)\) 了。
但是考虑到因为保证了 \(l\le p\le r\),所有与 \(r - l\) 有关的贡献都可以拆为 \((r - p) + (p - l)\),这样只需要设计两个 dp 数组 \(f_i, g_i\),其中 \(f_i\) 表示考虑了 \([1, i]\) 的划分 \(i\) 所在段的 \(p\) 就为 \(i\) 时的最小代价,\(g_i\) 表示考虑了 \([1, i]\) 的划分 \(i\) 所在段的 \(r\) 为 \(i\) 时的代价。\(g_{j - 1}\to f_i\) 时处理 \(l = j, p = i\) 的 \((p - l)\) 部分的贡献,\(f_j\to g_i\) 时处理 \(p = j, r = i\) 的 \((r - p)\) 部分的贡献。具体系数可以看 F1 代码。
直接 dp 是 \(\mathcal{O}(n^2)\) 的,可以通过 F1。观察一下发现两个转移式都可以写成斜率优化的形式,使用李超线段树优化即可做到 \(\mathcal{O}(n\log n)\),就可以通过 F2 了。
CF2125 F - Timofey and Docker
"docker" 这个词所有字母都不一样,所以不存在一个字母能处在 \(> 1\) 个匹配中。
首先求出原串的 "docker" 数量,记为 \(c\),很自然的想法是从 \(c\) 开始变少 "docker" 的数量,变多 "docker" 的数量代价都是增加的,并且 \((i, f(i))\) 应该是下凸的(\(f(i)\) 表示 \(i\) 个 docker 的修改代价)。
于是找到 \(c\) 后最优的 "docker" 数一定是覆盖最多次且比 \(c\) 小最大的或比 \(c\) 大最小的,就只需要对这 \(2\) 种情况求解答案。
又因为 \(f(i)\) 是下凸的,使用 wqs 二分内层就只需要跑一个线性的 dp,我的写法是记 \(f_{i, 0\sim 5}\) 表示 \([1, i]\),此时 \(i\) 这个后缀匹配到 "docker" 的前缀长度为 \(0\sim 5\) 的最小代价。
时间复杂度 \(\mathcal{O}(n\log n)\)。
实际上对于 \(i < c\) 有 \(f(i) = c - i\),因为可以把现成的 "docker" 中的任意一个字符换掉,这部分不需要 wqs 二分求答案。
QOJ 6462 - Jewel Thief
因为 \(b\) 的值域很小,所以尝试复杂度带 \(b\),那就独立开每个重量,每次一次处理所有重量。
对于一个重量 \(b\),选取的方式一定是降序排序后依次选,这就说明记 \(s(i)\) 为选 \(i\) 个物品的价值,\(s(i)\) 应该是上凸的(\(s(i) - s(i - 1) \ge s(i + 1) - s(i)\))。
考虑对模 \(b\) 同余的一组 dp 值拉出来做转移,因为模 \(b\) 不同余的下标的转移一定不会影响,且这样的复杂度仍然为 \(\mathcal{O}(b \times k / b) = \mathcal{O}(k)\)。
对于一组 dp 值 \(f_{0\sim L}\),转移后的 \(f'_i = \max\limits_{j = 0}^i (f_j + s(i - j))\)。而因为 \(s(i)\) 是个上凸函数,转移具有决策单调性,考虑 \(i < j\le k\),若 \(f_j + s(k - j)\ge f_i + s(k - i)\),则对于 \(k\to k + 1\) 因为 \(s(k + 1 - j) - s(k - j)\ge s(k + 1 - i) - s(k - i)\),由 \(j\) 转移只会更优。
于是可以直接套用决策单调性的优化方法优化这个 dp 做到单组 \(\mathcal{O}((k / b)\log (k / b))\),一个 \(b\) 的复杂度就是 \(\mathcal{O}(k\log k)\),总复杂度为 \(\mathcal{O}(\max b\times k\log k)\)。
分治写法应该很好写,我写的是维护决策队列。
浙公网安备 33010602011771号