2022HDU多校08
A
由于只能选择奇数长度的段进行反转,因此奇偶性不同的两个位置永远不能实现交换。
而取长度为 \(3\) 的段总能使相邻两个奇偶性相同的位置进行交换,因此只需对奇子列和偶子列分别排序即可。
B
记 \(f_{i}\) 表示考虑前 \(i\) 个位置的划分方案数。则 \(f_{i} = \sum\limits_{1 \le j \le i \and [j, i] \text{ is ridiculous}}f_{j - 1}\)。
问题就是如何快速找到那些满足条件的 \(j\)。
为了解决 “最大值在哪” 这个问题,维护一个值递减的单调栈(存位置),记单调栈内相邻两个元素为 \(x, y(x < y)\),则 \(\forall j \in (x, y]\),\(\max\limits_{j \le k \le i}a_{k} = a_{y}\)。
为了解决 “最大值位置为奇数” 这个问题,对奇数位置和偶数位置分别维护(开两棵线段树)。\(\forall j \in (x, y]\),若 \(j\) 与 \(y\) 奇偶性相同,则 \([j, i]\) 能够满足最大值的条件。
最小值同理。
由于只有同时满足最大最小值的 \(j\) 才能将 \(f_{j - 1}\) 转移给 \(f_{i}\),因此不妨维护 \(cnt\) 表示区间内某个位置满足条件个数的最大值,\(sum\) 表示 \(cnt\) 最大的所有位置的 \(f\) 值之和。对于当前 \(f_{i}\),若一棵线段树全局 \(cnt\) 值为 \(2\),则 \(f_{i}\) 加上这棵线段树的全局 \(sum\) 值。更新单调栈时同时维护相应线段树上的信息。
答案为 \(f_{n}\)。
C
神仙搜索,神仙状压。
对每个位置预处理出以它为起点的所有可行回文路径,路径以二进制形式存储。可以想到有效的路径数量并不多。
问题转化为:有若干二进制状态,求用这些状态不重不漏地拼出全集 \(2^{nm} - 1\) 的方案数。
直接进行记忆化搜索,每次选取二进制下最低位作为起点,枚举不与当前状态相交的路径进行递归。
经检验,\(6 \times 6\) 的全 a 矩阵有效状态数为 \(2 \times 10^{6}\) 出头。
时限 15s,放心跑。
D
签到题,直接猜答案为 \(2n\)。
证明:先证明答案下界为 \(2n\):若没有与坐标轴平行的直线,则一条直线最多穿过 \(2\) 个边界点,而边界上共有 \(4n\) 个点,因此至少需要 \(2n\) 条直线;否则,假设画了 \(k_{1}\) 条横线、\(k_{2}\) 条竖线,然后划归为一个子问题,同样能说明下界为 \(2n\)。
而 \(x + y = 1, 2, \dots, 2n\) 显然是一组直线数量为 \(2n\) 的合法解,因此答案就是 \(2n\)。
E
只需预处理出每个位置能到达的范围即可 \(O(1)\) 查询,记第 \(i\) 个位置的最大可达范围为 \([L_{i}, R_{i}]\),初始化 \(L_{i} = R_{i} = i\)。
(注意 \([L_{i}, R_{i}]\) 是不断被更新的;两个位置是否可达也要考虑当前它们已经能到达的区域)
先规定只能向右走,从后往前枚举,求出每个位置向右能走到的最远位置。
具体地:
-
如果 \(i\) 能到达 \(i + 1\),则 \(R_{i} = R_{i + 1}\)。
-
这还没完,\(R_{i}\) 有可能比 \(R_{i + 1}\) 更大,因为 \(a_{i}\) 可能贡献出有助于扩展的质因子。所以让 \(R_{i}\) 继续向后跳,直到无法扩展为止。
分析一下:我们记不能跳的地方为 “断点”,有 \(O(n)\) 个断点。当一个断点能够被跳过去,它就不会再被遍历。
然后再从前往后枚举,求出每个位置的最大可达范围。
具体地:
-
如果 \(i\) 与 \(i - 1\) 互达,则直接令 \([L_{i}, R_{i}] = [L_{i - 1}, R_{i - 1}]\),因为该情况下 \(i\) 与 \(i - 1\) 能到达的范围一定相同。
-
如果 \(i\) 无法到达 \(i - 1\),则位置 \(i\) 无法往前走,则它能到达的范围只能是 \([i, R_{i}]\)。
-
如果 \(i\) 能到达 \(i - 1\) 但 \(i - 1\) 不能到达 \(i\)(即 \(R_{i - 1} = i - 1\)), 则从位置 \(i\) 向左右扩展,方法类似上述的只向后扩展的做法。
(还不会证这个情况时间复杂度QWQ)(别急,有反转)
判断是否可达:可以用 vector 存下每个质数的出现位置,在相应的 vector 上二分查询一段区间内是否出现过该质数。
upd:官方的做法是错的,s下面这组数据能够卡掉下发的 std(来源于知乎):
#include <iostream>
#include <cstdio>
using namespace std;
int main () {//freopen("rand.txt", "w", stdout);printf("1\n");printf("200000 1\n");for (int i = 1; i <= 200000; i ++) printf("%d ", (i & 1) ? (((i + 1) % 4 == 0 ? 5 : 2)) : 3);printf("\n");for (int i = 1; i < 200000; i ++) printf("%d ", (i & 1) ? 3 : ((i % 4 == 0 ? 5 : 2)));printf("\n");printf("1 200000\n");return 0;
}
问题还是出在 \(i\) 能到达 \(i - 1\) 且 \(R_{i - 1} = i - 1\) 的情况,官方的 std 能被卡成 \(O(n^{2}\log{n})\)。
to be done.
G
先构造出这样一棵生成树:\(i\) 与 \(i + 1\) 连边,这样每条边的边权 \(\le n - 1\)。
由 Kruskal 算法的原理知,最小生成树的边权一定 \(\le n - 1\),即有用的边一定满足 \(|i - j| \times |p_{i} - p_{j}| \le n - 1\)。
则一定有 \(|i - j| \le \sqrt{n - 1}\) 或 \(|p_{i} - p_{j}| \le \sqrt{n - 1}\)。
而 \(p\) 是 \((1, 2, \dots, n)\) 的一个排列,所以有用的边数是 \(O(n\sqrt{n})\)。
可以在 \(O(n\sqrt{n})\) 的时间复杂度找出这些边再做 Kruskal 算法,时间复杂度 \(O(n\sqrt{n}\alpha(n))\)。
不要用 sort,\(O(n\sqrt{n}\log{n})\) 是无法接受的(可以用自然排列卡掉)。由于边权很小,所以直接用桶排即可。
还有注意要开 long long。
H
记 \(f_{u, 0 / 1/ 2}\) 分别表示仅考虑 \(x\) 子树,选择 \(x\) 且 \(x\) 度数为 \(0\)、选择 \(x\) 且 \(x\) 度数为 \(1\)、不选 \(x\) 的合法最大分离集大小。
然后做个简单的线性树形 DP 即可。
I
考虑 \(u \to fath_{u}\) 这一条边对答案的贡献。
如果在 \(fath_{u}\) 的子树内,操作 \((v, c, p)\)(其中 \(v\) 在 \(u\) 的子树内)是最后一个被执行的,则 \(u \to fath_{u}\) 的颜色为 \(c\)。
考虑以操作时间作为下标建立线段树,对区间 \([l, r]\) 维护 \(prod =\prod\limits_{l \le i \le r}(1 - p_{i})\) 与 \(sum = \sum\limits_{l \le i \le r}c_{i}p_{i}\max(1, \prod\limits_{i < j \le r}(1 - p_{j}))\),前者用于方便计算 \(sum\),后者即为我们希望得到的期望值。
对操作 \((x_{i}, c_{i}, p_{i})\) 初始化线段树 \(root_{x}\) 的第 \(i\) 个位置,然后做线段树合并。
合并到以 \(u\) 为根的子树时,统计 \(u\) 子树内除了在点 \(u\) 处之外的所有操作对 \(v \to u(v \in son_{u})\) 的贡献。
这些贡献加起来就是整棵树的期望边权和。
K
枚举某一维的边长即可。
L
显然 \(k\) 为奇数时无解。
不妨转化为数列问题:构造一个有限数列 \(\{ a_{1}, a_{2}, \dots, a_{d} \}\),数列需要满足以下条件:
-
\(a_{1} \le 1\)
-
\(a_{i} \le 2a_{i - 1}\)
-
\(w = \sum\limits_{1 \le i \le d}ia_{i} = \frac{k}{2}\)
数列 \(a\) 对应树的节点数为 \(1 + 2\sum\limits_{1 \le i \le d}a_{i}\),数列 \(a\) 的字典序关系与深度序列的字典序关系一致。
为了最小化节点数,即最小化 \(s = \sum\limits_{1 \le i \le d}a_{i}\),一个显然的思路是令所有 \(a_{i}\) 尽量等于 \(1\),这样一来可以确定 \(d\) 的上界是 \(O(\sqrt{n})\) 级别的。
不妨设有一个全 \(1\) 数列,且其项数 \(d\) 满足 \(\sum\limits_{1 \le i \le d}i = \frac{d(d + 1)}{2} \le \frac{k}{2}\) 即 \(d(d + 1) \le k\)。
如果 \(d(d + 1) = k\):该结构就是合法的最优解,直接输出即可。
否则,数列 \(a\) 必须进行一定的改动。比如,\(s \ge d + 1\)。
如果仍然在 \(d\) 项全 \(1\) 数列的基础上思考,并考虑如何让 \(a_{i} = 1\) 变成 \(a_{i} = x\),那么问题会很棘手。
项数 \(d\) 不应该成为绊脚石,全 \(1\) 数列不应该成为绊脚石,毕竟它们不是答案的第一决定因素。
但这并不意味着先前的工作一无是处。比如,我们知道了 \(s \ge d + 1\)。
那就让 \(s\) 来主导思考的方向吧!
一个 \(s\) 值可以确定 \(w\) 的上下界:下界的情况就是把深度较浅的节点填满,上界的情况就是全 \(1\) 数列。为方便表示,记 \(V_{w}(s)\) 表示 \(s\) 所确定的 \(w\) 的取值集合。
我们可以肯定:\(K = \frac{k}{2}\) 小于 \(V_{w}(d + 1)\) 的上界。
那么下界如何呢?
- \(\inf{V_{w}(1)} = 1\),\(\sup{V_{w}(1)} = 1\)
- \(\inf{V_{w}(2)} = 3\),\(\sup{V_{w}(2)} = 3\)
- \(\inf{V_{w}(3)} = 5\),\(\sup{V_{w}(3)} = 6\)
- \(\inf{V_{w}(4)} = 8\),\(\sup{V_{w}(4)} = 10\)
- \(\inf{V_{w}(5)} = 11\),\(\sup{V_{w}(5)} = 15\)
- \(s \ge 5\) 时:总有 \(\sup{V_{w}(s)} \ge \inf{V_{w}(s + 1)} - 1\)
即:除 \(K = 2, 4, 7\) 即 \(k = 4, 8, 14\) 外,\(K = \frac{k}{2}\) 一定介于 \(V_{w}(d + 1)\) 的上下界之间。
事情的发展开始变得清晰;我们很清楚我们希望看到怎样的结论:\(V_{w}(s)\) 是一段连续的值域。
正确性显然!从下界的情况出发,每次选取最大的 \(a_{i} > 1\),令 \(a_{i + 1}\) 加一,\(a_{i}\) 减一。在到达上界之前,该操作总可以被执行,\(a\) 序列也总是合法,且 \(w\) 每次增加 \(1\)。
也就是说,\(s = d + 1\) 时一定有解!
于是我们证明了答案的存在性。
接下来,为了最小化序列字典序,我们希望深度较浅的节点数较少,即序列 \(a\) 中靠前的元素值较小。
假设我们已经确定了 \(a_{1} \sim a_{i - 1}\),令 \(s^{'} = s - \sum\limits_{1 \le j < i}a_{j}\),\(K^{'} = \frac{k}{2} - \sum\limits_{1 \le j < i}ja_{j}\)。
此时 \(s^{'}\) 当然也确定了 \(w^{'} = \sum\limits_{j \ge i}ja_{j}\) 的上下界,并且 \(w^{'}\) 值域连续,原理同上。我们假定在之前的构造过程中,保证进行到这一步时:\(s^{'}\) 可以确定出一段 \(w^{'}\) 的值域包含 \(K^{'}\)。初始条件即 \(s = d + 1, K = \frac{k}{2}\),显然满足。
但是,\(s^{'}\) 具体(即考虑了 \(a_{i}\) 之后)决定了 \(w^{'}\) 的一段怎样的值域,还要取决于 \(a_{i}\) 的取值。
一个显然的性质是:\(w^{'}\) 的值域上下界均关于 \(a_{i}\) 单调不增。
如果 \(a_{i}\) 过小,\(K^{'}\) 可能小于 \(w^{'}\) 的下界;如果 \(a_{i}\) 过大,\(K^{'}\) 可能大于 \(w^{'}\) 的上界。
我们希望字典序尽量小,即 \(a_{i}\) 取一个使得 \(K^{'}\) 在 \(w^{'}\) 值域内的最小值即可;由于先前的限制,这样的 \(a_{i}\) 一定存在,且是以最优解存在。(所以我们不需要考虑上界;上界一定满足条件)
根据上述类似数学归纳法的算法流程,合法且字典序最小的序列被唯一确定。
时间复杂度 \(O(T\sqrt{k}\log{k})\)。