代码源挑战赛 Round 57 题解 - 代码源竞赛平台

T1

100%

只需要输出任意在 1818 到 120120 以内的整数即可。

时间复杂度 O(1)O(1)。

T2

100%

用一个变量 pospos 表示熊大当前的跑道编号,初始值为 xx。依次遍历操作字符串 ss 的每个字符:

  • 如果是 L,则有 pos=max⁡(0,pos−1)pos = \max(0, pos - 1)。
  • 如果是 R,则有 pos=min⁡(m,pos+1)pos = \min(m, pos + 1)。
  • 时间复杂度:O(n)O(n)。

T3

100%

考虑展开“合理值”的公式:

$$\begin{aligned} & (x_1 - l)^2 + (x_2 - l)^2 + (x_3 - l)^2 +\cdots (x_m -l)^2 \\ =& (x_1^2 - 2x_1l + l^2) + (x_2^2 -2x_2l + l^2) + (x_3^2 -2x_3l + l^2) + \cdots + (x_m^2 -2x_ml + l^2) \\ = & (x_1^2 + x_2 ^2 + \cdots + x_m^2) - 2l(x_1 + x_2 + x_3 + \cdots + x_m) + m l^2 \end{aligned}$$

这说明,只要我们知道区间 [l,r][l, r] 内成绩的个数(mm)、总和(x1+x2⋯+xmx_1 + x_2 \cdots + x_m)以及平方和(x12+x22+x32⋯xm2)(x_1^2 + x_2^2 + x_3^2 \cdots x_m ^2 ),就可以在 O(1)O(1) 的时间内算出答案。

所以可以将原数组先从小到大排序,后根据排序后的数组利用前缀和计算出总和和平方和。每次询问可以利用二分查找来找到最小的 ≥l\ge l 的下标和最大的 ≤r\le r 的下标,再利用上面的公式进行计算。

时间复杂度 O((n+q)log⁡n)O((n + q) \log n)。

T4

50%

考虑动态规划。

设 dp[i][u][v]dp[i][u][v] 表示:已经输入了前 ii 个字符,当前左手食指在键盘位置 uu(在 aua_u),右手食指在键盘位置 vv(在 ava_v)时的最小移动距离和。

为了输入当前字符 sis_i(假设它在 aa 中的位置 nownow),我们只有两种的操作:

  1. 左手去按: 左手从 uu 移动到 nownow,右手停留在 vv 不动。 $dp[i][now][v] = \min(dp[i][now][v], dp[i-1][u][v] + \min(|now- u|,26 - |now- u|))$
  2. 右手去按: 右手从 vv 移动到 nownow,左手停留在 uu 不动。 $dp[i][u][now] = \min(dp[i][u][now], dp[i-1][u][v] + \min(|now- v|,26 - |now- v|))$

初始状态为 dp[0][0][0]=0dp[0][0][0] = 0。答案为 dp[n][i][j]dp[n][i][j] 的最小值。

时间复杂度为 O(n×26×26)O(n \times 26 \times 26)。

100%

与 50%50\% 的做法类似,但是我们可以发现观察到无论怎么移动,在输入完第 ii 个字符时,必然有一根食指停留在字符 sis_i 的位置。因此,我们只需要记录“另一根食指的位置”即可构成完整的状态。

设 dp[i][j]dp[i][j] 表示已经输入了前 ii 个字符,且当前一根食指在 sis_i 字符所在的位置上,另一根食指在 aa 的位置为 jj 时的最小移动距离和。

当前我们要输入 sis_i(假设它在 aa 中的位置 nownow),上一时刻一根手指在 si−1s_{i-1}(假设它在 aa 中的位置 uu),另一根在 jj(它在 aa 中的位置 jj)。我们有两种选择:

  1. 移动停在 uu 的手指去按 nownow:那么另一根手指仍然在 jj。
  • $dp[i][j] = \min(dp[i][j], dp[i-1][j] + \min(|now- u|,26 - |now- u|)))$
  1. 移动停在 jj 的手指去按 nownow:那么另一根手指的位置变成了 uu。
  • $dp[i][u] = \min(dp[i][u], dp[i-1][j] + \min(|now-j|,26 - |now- j|))$

初始状态为 dp[0][0]=0dp[0][0] = 0。

时间复杂度为 O(n×26)O(n\times 26)。

本题也可以在 O(n×26×26)O(n \times 26 \times 26) 常数小的时候通过。

T5

100%

由于 TT 高达 10510^5,这就要求我们要先预处理出所有答案。

考虑 x2−y2=z3x^2 - y^2 = z^3。根据平方差公式,可以拆解为:

(x+y)(x−y)=z3(x + y)(x - y) = z^3

我们令 u=x+yu = x + y,v=x−yv = x - y。显然有 u⋅v=z3u \cdot v = z^3。 根据 uu 和 vv,我们可以反推 xx 和 yy:

x=u+v2,y=u−v2x = \frac{u + v}{2}, \quad y = \frac{u - v}{2}

为了保证 xx 和 yy 均为正整数,必须满足两个条件:

  • uu 和 vv 必须同奇偶(即 (u+v)≡0(mod2)(u + v) \equiv 0 \pmod 2)。
  • u>vu > v(以保证 y>0y > 0),并且计算出的 x≤nx \le n。

那么也就是说,只要我们知道 z3z^3 的所有因数,就可以快速求出所有满足条件的 (x,y,z)(x,y,z)。

具体计算方式如下:

题目要求 x≤nx \le n,当 n=106n = 10^6 时,最大的 x2x^2 是 101210^{12}。 因为 y≥1y \ge 1,所以 z3=x2−y2<x2≤1012z^3 = x^2 - y^2 < x^2 \le 10^{12}。 这意味着 z≤10123=104z \le \sqrt[3]{10^{12}} = 10^4。 最大的 zz 只有 1000010000 级别,因此我们可以暴力枚举所有的 zz,去计算 z3z^3 的所有可能分解方式。但是直接用 O(z3)O(\sqrt{z^3}) 的方法显然是不行的,我们需要一些其他方法。

首先我们可以在 O(z)O(\sqrt{z}) 算出 zz 的质因数分解:

z=p1e1p2e2…pkekz = p_1^{e_1} p_2^{e_2} \dots p_k^{e_k}

那么 z3z^3 的质因数分解其实就是:

z3=p13e1p23e2…pk3ekz^3 = p_1^{3e_1} p_2^{3e_2} \dots p_k^{3e_k}

有了这个分解式,我们可以利用 dfs 直接枚举出 z3z^3 的所有因子,具体来说,我们只需要枚举一个长度为 kk 的数组 (c1,c2,c3,⋯ck)(c_1 ,c_2 ,c_3 ,\cdots c_k),满足 0≤ci≤3ei0 \leq c_i \leq 3e_i。那么

p1c1p2c2…pkckp_1^{c_1} p_2^{c_2} \dots p_k^{c_k}

它就是一个 z3z^3 的一个因子。对于每个因子 uu,只需要根据上述方法计算出对应的 (x,y,z)(x,y,z) 即可。设 cnt[i]cnt[i] 表示 x,y,zx,y,z 中最大值为 ii 时满足条件的三元组个数(实际上 x>max⁡(y,z)x > \max(y,z)),如果 x≤106x \leq 10^6,我们就可以让 cnt[x]cnt[x] 加上 11,最后做一次前缀和即可。对于每次查询,我们只需要输出 cnt[n]cnt[n] 的结果就好。

总体时间复杂度为 O(∑i=1N23d(z3)+T)O(\sum_{i = 1}^{N^{\frac{2}{3}}}d(z^3) + T)。

T6

100%

考虑分析排序规则:我们可以将物品移到最前或最后。为了使移动代价最小,我们应该保留一个代价最大的子序列不动,然后将其余的元素分别移到它的左边和右边。

什么样的子序列可以“不移动”且能保证其余元素移完后整体有序? 它必须满足在数值上是连续的(如标号 L,L+1,…,RL, L+1, \dots, R),并且在当前数组中的相对位置是从左到右递增的(即 pos[L]<pos[L+1]<⋯<pos[R]pos[L] < pos[L+1] < \dots < pos[R])。

因此,问题的本质是:在数值 1…n1 \dots n 构成的序列中,找到一个连续的区间 [L,R][L, R],满足对于任意 k∈[L,R−1]k \in [L, R-1] 都有 pos[k]<pos[k+1]pos[k] < pos[k+1],且使得该区间的代价和 ∑k=LRWk\sum_{k=L}^R W_k 最大。 最终答案即为:所有物品的总代价 ∑W−max⁡(合法区间代价和)\sum W - \max(\text{合法区间代价和})。

我们可以考虑用线段树维护连续子段最大和(做法与区间最大子段和类似):

我们用物品的标号 1…n1 \dots n 作为线段树的底层叶子节点。

线段树的每个节点维护区间 [l,r][l, r] 内的以下信息:

  1. sumsum: 该数值区间所有物品的 WiW_i 之和。
  2. prepre: 从左端点 ll 开始,满足 pospos 递增的最长合法前缀的权值和。
  3. sufsuf: 以右端点 rr 结束,满足 pospos 递增的最长合法后缀的权值和。
  4. mxmx: 该数值区间内,完全合法的子段的最大权值和。
  5. incinc: 该数值区间 [l,r][l, r] 是否整体合法(即 pos[l]<pos[l+1]⋯<pos[r]pos[l] < pos[l+1] \dots < pos[r])。

当合并左子节点(区间 [l,mid][l, mid])和右子节点(区间 [mid+1,r][mid+1, r])时,连接点是 midmid 和 mid+1mid+1。 当且仅当 pos[mid]<pos[mid+1]pos[mid] < pos[mid+1] 时,左区间的后缀与右区间的前缀可以拼合。 通过标准的子段和维护套路更新对应变量即可。这里不再赘述。

  • 时间复杂度:建树 O(nlog⁡n)O(n \log n),每次修改和查询只需 O(log⁡n)O(\log n)。总时间复杂度为 O((n+q)log⁡n)O((n+q)\log n)。