The 4th Universal Cup. Extra Stage 7: Hubei
A. Sort
先判掉序列本身有序的情况。
先枚举第一次的位置,假设此时是 \(p\),那么此时 \([1, p], (p, n]\) 都是有序的。
考虑第二次位置 \(q\),假设 \(q > p\),那么 \([1, q], (q, n]\) 操作完后是有序的,且 \((q, n]\) 就会是这次操作前(\(p\) 操作后的)在 \((q, n]\) 的那些值,此时 \([1, q]\) 就是操作前的 \([1, p], (p, q]\) 两个区间归并起来的值。
此时没排好序当且仅当 \(q\) 处的值 \(>\) \(q + 1\) 处的值,且这两个值恰好就是 \([1, q], (q, n]\) 两部分的最大最小值。
这启发去关注左侧最大值和右侧最小值的变化,会发现若 \(q > p\),则若还没排好序则左侧最大值是不变的,右侧最小值变为 \(q + 1\) 处的值;同样的,若 \(q < p\) 且还没排好序,右侧最小值不会发生改变,而左侧最大值变为 \(q - 1\) 处的值。且记右侧最小值为 \(i\) 左侧最大值为 \(j\),发现左右侧值在 \([i, j]\) 的部分在排序后从来没有发生过改变。
于是可以设计一个 dp,\(f_{i, j}\) 表示右侧最小值为 \(i\) 左侧最大值为 \(j\) 的期望步数(需要满足 \(i < j\))。转移有 \(4\) 种可能:
- \(q = p\)。\(i, j\) 都不发生改变。
- \(q < p\) 且 \(q - 1\) 处的值 \(> i\)。则左侧最大值会变成 \(q - 1\) 处的值。
- \(q > p\) 且 \(q + 1\) 处的值 \(< j\)。则右侧最小值会变成 \(q + 1\) 处的值。
- 非上面 \(3\) 种情况。操作后有序。
直接 dp 是 \(\mathcal{O}(n^3)\) 的,注意到 \((i, j)\) 能贡献到的是 \((i, j'), (i', j)\),有一维是固定的,于是可以把转移改成 \((i, j)\to (i, *)\to (i, j')\) 的形式,这样就可以做到单轮 \(\mathcal{O}(n^2)\),总复杂度 \(\mathcal{O}(n^3)\) 了。
当然这个题可以进一步做到 \(\mathcal{O}(n^2)\)。
首先把求解的值转化一下,转换成到达每个状态 \((i, j)\) 的概率之和,这里有个问题是转移是可能走自环的,但是因为每个状态走自环的概率都是 \(\frac{1}{n}\),只需要给最终答案乘上 \(\frac{n}{n - 1}\),并把走到其他状态的概率换为 \(\frac{1}{n - 1}\) 就好了(如果去写一下 \(\mathcal{O}(n^3)\) 的做法也会发现这件事)。
然后能发现到达一个合法状态 \((i, j)\) 的概率只与在第一次操作后右侧 \(< i\) 的数和左侧 \(> j\) 的数的数量有关,于是可以在外层 \(\mathcal{O}(n^2)\) 算出每个状态的概率,内层枚举 \(p\) 后可以利用前缀和 \(\mathcal{O}(n)\) 算出答案,总复杂度为 \(\mathcal{O}(n^2)\)。
B. Sequence Operations
一旦经过了 \(\operatorname{mex}\) 操作,集合中的元素就只可能 \(\in {0, 1, 2}\)。且不断对一个数取 \(\gcd\),只有 \(\mathcal{O}(\log V)\) 是有用的。
于是前缀集合在去重之后是可以暴力维护下来的,因为若过程中没有做过 \(\operatorname{mex}\) 操作则只会有 \(1\) 个集合,大小至多为 \(n\),否则考虑最后一次取 \(\operatorname{mex}\) 的位置,从这个位置开始不断取 \(\gcd\),可能的 \(\gcd\) 值只可能有 \(\mathcal{O}(\log V)\) 种,所以只会有 \(\mathcal{O}(\log V)\) 种大小 \(\le 3\) 的集合。
每经过一个操作暴力对每个可能集合都操作再去重就好了,需要注意唯一的大集合在做 \(\operatorname{mex}\) 操作时不能全部遍历一遍,而应该只选出最小的 \(3\) 个数,这样已经可以代表整个集合了,取 \(\gcd\) 时也应该只操作那些可能使集合发生变化的操作,只需要维护一个前缀 \(\gcd\),只有前缀 \(\gcd\) 改变时再对集合做操作。
复杂度 \(\mathcal{O}((n + m)\log^2 V)\)。
C. Believe in You
容易知道具体策略:如果在这一轮可以击败对手则直接击败,否则先防御住对手的攻击再尽可能地多打点伤害。
时间复杂度 \(\mathcal{O}(n)\)。
D. D. Prime Game
打个表或者感受一下,\(n\) 增加时答案一定是不减的。
可以归纳证明,因为 \(n - 1\) 先手可以选择的范围为 \([n - 1, 2n - 3]\),而 \(n\) 可以选择的范围为 \([n, 2n - 1]\),唯一选不了的 \(n - 1\) 可以用 \(2(n - 1)\) 来替换,根据归纳一定不劣。
所以后手的策略就是每次除最大的质因子,先手的策略就是让后手操作后得到的值尽量大。
一个想法就是每次变成 \(\ge n\) 的最小的 \(2\) 的幂次,这样下一轮的 \(n'\) 至少是 \(\frac{n}{2}\) 的,继续发现先手选择的数也可能有 \(3\),因为 \(\frac{2}{3} > \frac{1}{2}\),不过因为 \(\frac{2}{5} < \frac{1}{2}\),所以如果带有 \(5\) 的质因子一定不优。
于是只需要枚举在 \([n, 2n)\) 中只含 \(2, 3\) 的数就行了。
复杂度 \(\mathcal{O}(\log^3 V)\)。
E. Rook
如果对行列容斥会发现不好处理不可重的限制也会因为两个求和式不好优化,于是转为对不可重容斥。
考虑若不考虑不可重的限制,记 \(i\) 个棋子的方案为 \(g_i\),对行列独立开计数,方案数为 \({n\brace i}\times {m \brace i}\)。
考虑上不可重的限制,记 \(f_i\) 为 \(i\) 个棋子的答案,有 \(g_i = \sum\limits_{j = 0}^i {i\brace j}f_j\)。
考虑斯特林数容斥,就有 \(f_i = \sum\limits_{j = 0}^i (-1)^{i - j} {i \brack j} g_j\)。
因为只求 \(f_n\),只需要求出 \({n\brace *}, {m\brace *}, {k \brack *}\) 的值。
我是 poly 低手,所以没有实现。
F. Lottery
记 \(f_i\) 是 \([i, +\infty)\) 轮的期望贡献。
对 \(a\) 按值排序,转移枚举 \(p\) 表示若随出来的物品的编号 \(\le p\) 就重掷,所以 \(f_i = \frac{1}{n}\max\limits_{p = 0}^n (pf_{i + 1} + \sum\limits_{j = p + 1}^n (a_j - c - k\frac{i(i + 1)}{2}))\)。注意到对于每个 \(p\) 的值都 \(\le \sum\limits_{j = 1}^n \max(f_{i + 1}, a_j - c - k\frac{i(i + 1)}{2})\) 且一定有 \(p\) 能取到(因为 \(a_j - c - ki\) 是单调的,所以 \(f_i = \sum\limits_{j = 1}^n \max(f_{i + 1}, a_j - c - k\frac{i(i + 1)}{2})\)。
且因为单调,可以 \(\mathcal{O}(\log n)\) 二分得到 \(p\),借助后缀和可以 \(\mathcal{O}(\log n)\) 实现一轮的转移。
但现在的 dp 是要基于操作轮数的,于是要尝试卡一下轮数。发现若 \(c + ik > \max a\),这一轮一定不会进行,因为就算选中了 \(\max a\) 相对上一轮选一个 \(0\) 的收益也是 \(\max a - c - ik < 0\)。
所以若 \(k\not = 0\) 只会有 \(\mathcal{O}(\max a / k)\) 轮,就可以 dp 了。
对于 \(k = 0\),因为每一轮的代价是一样的,所以在最优策略下每一轮选择的 \(p\) 都是相同的,枚举 \(p\),写出期望表达式 \(E = -k + pE + \sum\limits_{i = p + 1}^n a_i\),移项后可解得 \(E\)。
最终复杂度为 \(\mathcal{O}(n \log n + \max a\log n)\)。
G. I Will Always Remember You
首先可以 \(\mathcal{O}(nm / w)\) 求出一个点到达的颜色集合:
- \(C_u = \{a_u\} \cup \bigcup_{v\in e_u} C_v\)。
然后把询问按照 \(B\ge w\) 分块。
对于一个块,只有出现在块中的 \(\mathcal{O}(B)\) 种颜色的可达性会被改变,所以跑一个 \(\mathcal{O}(mB / w)\) 的可达性先去掉每个点到这 \(\mathcal{O}(B)\) 种颜色的可达贡献。
对于一个点和一个块间会被修改的颜色,这个颜色能贡献的情况有两种:
- 块中没有被修改的点,其颜色没有改变。
- 块中被修改的点。
于是可以再在 \(\mathcal{O}(mB / w)\) 的复杂度下跑出一个点到被修改的颜色但是不包括被修改的点的可达性和一个点到被修改的点的可达性。
这样对于一个点 \(u\) 的询问,可以 \(\mathcal{O}(B)\) 枚举被修改的点,就可以把修改的 \(\mathcal{O}(B)\) 种颜色的贡献算对了。
因为过程需要保证每次分块前 \(| C_u |\) 都是正确的,所以在处理完一个块后需要再跑一个 \(\mathcal{O}(mB / w)\) 的分块把修改完后 \(\mathcal{O}(B)\) 种颜色的贡献重算一遍。
时间复杂度 \(\mathcal{O}((n + q)m / w + qB)\)。
因为每一轮分块都需要预处理一些东西,所以取 \(B = w\) 实际效率并没有很优秀,我取的 \(B = 512\)。
H. Rectangle Cutting
显然行列是独立的,最大面积是行最长连续段长度乘列最长连续段长度。
用 set 维护下行列的分割点,再用 multiset 维护下每个连续段的长度即可。
时间复杂度 \(\mathcal{O}(q\log q)\)。
I. Nailoong vs. Bombloong 2
发现每次把一个点改变颜色,异色边的改变量是与异色邻点数和同色邻点数有关的。
于是发现可以按照 dfs 序来选 \(x_i\),这样除了根以外每个点改变后的同色邻点数都是 \(1\),就可以据此算出每个点的度数了。
知道了度数后又因为这是 dfs 序所以可以构造出整棵树,维护 dfs 时根到一个点的链的结构,加入一个点时若链底的邻点都已确定则弹出,直到链底的邻点还没有确定完全,当前点便是其儿子。
这样就可以做 \(m = n - 2\) 了,但是对于 \(m = n - 3\) 会发现有个问题,因为最后 \(3\) 个点的结构是不确定的,所以如果最后 \(3\) 个点前已知的是还有一个点有一条边要连出去,连出去后是 \((1, 2), (1, 3)\) 的人字形还是 \((1, 2), (2, 3)\) 的链是不知道的。
也就是如果剩下了一个大小为 \(3\) 的子树是无法判断的,于是尝试对 dfs 序进行一些微调使得最后不会剩下大小为 \(3\) 的子树,一个方法是保证根的最后一个子树是一个叶子节点,这样对于剩下的 \(3\) 个点至少已知最后一个点是一个单独的子树,最大的子树大小至多为 \(2\),且一定是靠前的子树,结构就确定了。
J. The Best Card
题目描述的过程相当于是从左到右维护一个列,每一个位置会产生列的元素个数的贡献,如果当前有元素就需要留下最底下的元素并把剩余元素推向下一列。
把这个问题转换一下:每次任意选择一个元素,从起点开始走遇到第一个没有被标记的点,把这个点标记,产生所经格子数的贡献。容易验证这两种方法的贡献是相同的,这个标记对应到第一种求法其实就是一个位置有没有元素留下。
这样转化后容易发现 \(k\) 次操作一定会操作在同一个位置上,且肯定是已经给定的起点。
因为可以按照任意顺序放置,所以可以按照起点位置从大到小放置,这样处理 \(k\) 个额外的贡献时没有后效性。
只需要实现一个从 \(s\) 开始得到前 \(x\) 个没被标记的位置的和并可能需要对其打上标记的操作,动态开点线段树就能解决这个问题。
时间复杂度 \(\mathcal{O}(n\log n + n\log V)\)。
K. Deletion Game
尝试判定一个删除区间的集合 \((l_{1, 2, \cdots}, r_{1, 2, \cdots})\) 能否被操作,会发现对于 \((l_i, r_i], (l_j, r_j]\) 必须要无交才可以操作。
先不考虑求解第二问,对于第一问就有了一个连边方法:\((i, i + 1, a_{i + 1}), (i, j, 0)(s_i = s_j)\),这样从 \(1\) 点以 \(a_1\) 的距离开始,到 \(n + 1\) 这个点的最短距离就是最小权重和。
然后会发现把第二问的代价一同塞入边权也是合理的:\((i, i + 1, (a_{i + 1}, 0)), (i, j, (0, [j\le i]))(s_i = s_j)\)。
优化有两种方法:
- 对于第二种边,会发现如果在 \(i\) 之前已经有 \(i'(s_{i'} = s_i)\) 处理过这类连边了,这类连边就不再需要考虑了。
- 对于 \(i < j < k(s_i = s_j = s_k)\),如果选择了 \((i, k]\),可以拆成 \((i, j], (j, k]\) 两个区间来考虑,代价不变,进一步的能发现对 \((i, j, k)\) 的循环同构都是满足的,于是对于第二类连边 \(i\) 只需要考虑向后遇到的第一个 \(s_j = s_i\) 的 \(j\)。
复杂度 \(\mathcal{O}(n\log n)\)。
L. String Matching
把 \(t_{i, j}\) 的 border 分为 3 类:
-
\(len \le |s_i|\),border 会在 \(i\) 是一个前缀,在 \(j\) 是一个后缀,哈希即可。
-
\(|s_i| < len \le |s_j|\),border 靠前的会包含 \(i\) 和 \(j\) 的前缀,靠后的是 \(j\) 的后缀。
考虑靠前的部分在 \(j\) 的前缀的部分,对应到靠后的部分可以知道这是 \(s_j\) 的 border,而前面的部分能与 \(s_i\) 匹配,所以可以跑一个 AC 自动机,以 \(s_i\) 为模式串,在 \(s_j\) 的 border \([k, |s_j|]\) 统计 \([1, k]\) 后缀能匹配上的 \(s_i\) 的数量。
-
\(len > |s_j|\),border 靠前的包含 \(i\) 整串和 \(j\) 的前缀,靠后的会包含 \(i\) 的后缀和 \(j\) 整串。依然尝试对应匹配,发现其类似于:\(s_i\) 中 \([1, p]\) 是一个 border,\(s_j\) 中 \([q, |s_j|]\) 是一个 border,并且 \(s_i\) 中 \((p, |s_i|]\) 和 \(s_j\) 中 \([1, q)\) 能匹配上,依然可以哈希解决。
时间复杂度 \(\mathcal{O}(\sum |s_i|\times |\Sigma|)\)。
M. Iroha and the Kingdom of Construction
考虑两种 \(\mathcal{O}(\sqrt n)\) 的构造:
- 用 \(1\) 个栈把所有元素的第一次出现串在一起,对于第二次出现按照第一次出现的顺序编号,每个栈可以选入一个上升子序列,根据 dilworth 需要的栈数为最长下降子序列长度,是 \(\mathcal{O}(\sqrt n)\) 的,构造可以维护每个最长上升子序列的结尾,每次加入一个元素选择能加入且最小的结尾加入(会发现这也就是最长下降子序列的求法)。
- 按 \(B\) 分块,按照第二次出现 \(B\) 个分为一组。对于第二次出现可以开 \(B\) 个栈,从前至后把每一组的 \(B\) 个元素叠加到 \(B\) 个栈上,这里的叠加指的是每个栈只放一个,是一层的样子。对于一组的第一次出现只需要把这 \(B\) 个放在一个栈就好了。这样需要 \(B + n/B\) 个栈,取 \(B = \sqrt n\),需要 \(2\sqrt{n}\) 个栈。
组合一下两种做法,第二种做法对第二次出现的组是一层铺开的,看着很浪费,考虑类似第一种方法,把一组内第二次出现按照第一次出现的顺序重编号,得到 \(\mathcal{O}(\sqrt B)\) 个上升子序列,这样就只需要 \(\mathcal{O}(\sqrt B)\) 个栈,每一组把这 \(\mathcal{O}(\sqrt B)\) 个上升子序列铺成一层即可。
此时每一组单独是合法的,对于两个不同的组第一次出现是单独的栈所以是合法的,第二次出现因为是按照第二次出现分组的,所以天然就有大小关系,也是合法的。
这样取 \(B = \mathcal{O}(n^\frac{2}{3})\),就有 \(\mathcal{O}(n^\frac{1}{3})\) 的构造。
做到官方题解中的 Bonus(\(180\) 个栈)只需要再微调一下。
记第 \(i\) 组的第一次出现的栈为 \(L_i\),第二次出现的若干个上升子序列为 \(R_{i, 1}, R_{i, 2}, \cdots\)。上文的构造相当于是构造出了 \(L_{1\sim n/B}, R_{1\sim n/B, 1\sim \mathcal{O}(\sqrt{B})}\),这样有点浪费的一点是 \(L_{1\sim B}\) 是单独开的,于是尝试把 \(L_i\) 也融入 \(R\) 的部分。
能够发现对于第 \(i\) 组和第 \(j\) 组(\(i < j\)),因为 \(j\) 组的第二次出现都在 \(i\) 组的第二次出现之后,所以自然的 \(j\) 组的第二次出现都在 \(i\) 组的第一次出现之后,这说明 \(L_i\) 是可以用来叠放 \(R_{j, *}\) 的。
于是就有了一个构造:构造出 \(L_{1\sim c}, R_{i, 1\sim i}(i\in [1, c])\),\(R_{i, 1\sim i - 1}\) 叠加到 \(R_{i - 1, 1\sim i - 1}\) 上,而 \(R_{i, i}\) 叠加到 \(L_{i - 1}\) 上,\(L_i\) 新开一个栈即可。注意这里因为不同的 \(i\) 在 \(R_{i, *}\) 能使用的栈的数量是不同的,所以不能定长分块,而是动态的如果加入这个元素会出现 \(> i\) 个上升子序列就令 \(i\gets i + 1\) 重新从 \(0\) 个上升子序列开始处理。
所用的栈数在 \(175, 176\) 左右。
浙公网安备 33010602011771号