做题记录(OI)

注:题号前打星的为非独立做出的题。

CF2005E Subtangle Game

E1

对于第 \(i\) 轮,必败态是容易确定的。那么设 \(f(i, x, y)\) 表示第 \(i\) 轮走 \((x, y)\) 的位置是否必胜。

转移:如果对于任意 \(x' > x, y' > y\)\(f(i + 1, x', y')\) 都必败,那么 \(f(i, x, y)\) 必胜。这样是 \(O(n^2)\) 的,可以记 \(g(i, x, y)\) 表示上述条件是否满足,这样就是 \(O(1)\) 的。

E2

把 E1 用 bitset 优化即可。不知道为何出题人没卡这种做法。虽然我还没卡过去。

CF1867E Salyg1n and Array

E1

读题不完整,自己两行泪。

\(k\) 个为一块分块。对于整块,直接问即可。手玩一下,可以发现散块可以一个一个元素问。形式化的:设散块长度为 \(l = n \bmod k\),按 \(n - l - k + 2, n - l - k + 3, \cdots, n - k + 1\) 的顺序问即可。

为什么对?因为此时 \([n - l - k + 2, n - k]\) 里面的元素会向后平移,而 \(l\) 为偶数,所以这些元素会被消掉。

*E2

E1 一个一个问是没有必要的。观察 E1 的问法中 \(a\) 的变化,可知直接问 \(n - \dfrac{l}{2} - k + 1, n - k + 1\) 也是对的。手模一下即知正确性。

*CF2078D Scammy Game Ad

这题实际上可以贪心。首先显然要把全部增加的人放到同一边。我们把加法看作 \(\times 1\)

在走第 \(i\) 道门时,先算出来总共增加多少人。然后往后面扫,如果左边的倍率多于右边,那就放到左边;否则放到右边。如果比不出来,那就跳过。

为什么对?因为每次新增的人都是独立的。

P9915 「RiOI-03」3-2

\([0, 2^n)\) 的 0-1 Trie 建出来,设每个节点的权值为它在 \([0, 2^n)\) 中出现的次数。询问的位置为 \(u\) 时,把包含 \(u\) 的同色连通块的权值和算出来,即为答案。

*AGC052A Long Common Subsequence

\(S_1 + S_1\) 中,前半部分有 \(N\)\(\texttt{0}\),后半部分有 \(N\)\(\texttt{1}\)。所以考虑 \(\texttt{00} \cdots \texttt{01} \cdots \texttt{11}\),但是长度不够。发现在选完这些字符后,一定会有一个 \(\texttt{0}\) 在最后,所以 \(S_1 + S_1\) 的答案就是 \(\texttt{00} \cdots \texttt{01} \cdots \texttt{110}\)

发现 \(S_2 + S_2, S_3 + S_3\) 同理,所以答案就是 \(\texttt{00} \cdots \texttt{01} \cdots \texttt{110}\)

*P11457 [USACO24DEC] Job Completion G

进行了反悔贪心的学习。

容易发现,当 \(s_1 + t_1 < s_2 + t_2\) 时,选前者更好。所以按 \(s_i + t_i\) 升序选择。设当前用时为 \(t\),如果 \(t > s_i\),那就让当前工作替换做过的工作中用时最长的。

*P7521 [省选联考 2021 B 卷] 取模

高明的诈骗题。

在已知模数 \(m\) 时,可以 \(O(n \log n)\) 算出答案。枚举 \(a_i\),找到最大的 \(< m - a_i\)\(a_j\),然后用 \(a_i + a_j\) 更新答案。还可能是两个最大值,也要用这个更新答案。可以加一个优化:把重复的跳过。

我们按大小倒序枚举 \(a_i\) 作为模数,更新答案。如果答案 \(\ge a_i\),那么停止,因为此时答案不可能变大。实际上这样只会枚举 \(O(\log V)\) 个,因为跳过了重复的数。

*P10455 Genius Acm

算 SPD 时一定是最大的和最小的作差,次大的和次小的作差,以此类推。

考虑贪心。维护已经分好段的部分 \([1, l)\),找最大的合法的 \([l, r]\),把它分成一段。如何找?容易发现 \(r\) 有单调性,所以可以二分。

但是二分是 \(O(n \log^2 n)\) 的,极难优化。发现二分可以换成倍增:每次找最大的合法的 \(2^i\),然后 \(r \gets r + 2^i\)。在算 SPD 时,不需要每次都重新排序,只需把无序部分排序,然后归并到一起,即为 \(O(n \log n)\)

想到没前途的做法时要及时换思路。

*P14180 「FAOI-R8」奶龙大战暴暴龙

有解时 \(n = m\),故设 \(n = m\)。判无解是容易的。

先进行离散化,把 \(a_i\) 用它在 \(a'\) 中的下标代替,这样就变成了排序问题。很明显,有重复元素时也没问题。

先把 \(1\)\(2\) 放到最前面,可以暴力模拟。我们可以平移 \((1, 2)\)\(3\) 的前面,把 \(3\) 拼到一起;然后再平移 \((1, 2, 3)\)\(4\) 的前面,把 \(4\) 拼到一起,以此类推。容易发现这样的操作数是 \(n\) 次。

实现时,记录每个元素的位置 \(p_i\) 与当前平移区间的位置 \([l, r]\)。发现 \(a_i\) 在未选中的点中的下标就是 \(i - d_i\),其中 \(d_i\)\(i\) 之前选中的点。所以开一个树状数组维护 \(d_i\)。平移到 \(a_i\) 时,令 \(l \gets i - d_i\),然后 \(d_i \gets d_i + 1\) 即可。

建议画图辅助理解。

*P14309 【MX-S8-T2】配对

发现 \(\sum c_i\) 为奇数时有一个点没用,我们把这个点删去。交换两个 \(c_i\) 相当于删一个点再加一个点。

先做 \(\sum c_i\) 为偶数且无交换。考虑以 \(u\) 为根的子树,设当前子树中有 \(s_i\)\(1\),且 \(u\) 的父亲为 \(f_s\)。若 \(s_i\) 为偶数,那么这个子树内可以两两操作;否则就要有一个和子树外的一个点操作。所以当 \(s_i\) 为奇数时,\(\{ f_s, s \}\) 的边要选。

那其他情况怎么办呢?考虑 DP,令 \(f_{i, j, k}\) 表示在以 \(i\) 为根的子树中,删了 \(j \in [0, 2]\) 个点,新增了 \(k \in [0, 1]\) 个点。当 \(2 \nmid s_i - j + k\) 时就要把 \(\{ f_s, s \}\) 的边选上,以此转移,类似树上背包。

*P14310 【MX-S8-T3】图排列

发现最短路径一定是简单路径,否则可以把环去掉,这样一定不劣。

特殊性质 F

只需判定有没有只走 \([1, 2, 3, 4, 5]\) 的路径,如果有答案就是 \(1\),否则是 \(2\)

特殊性质 G

此时权值只有 \(6\) 种,\(S_{\mathrm{path}}\) 只有 \(2^6 = 64\) 种。所以对每一种 \(S_{\mathrm{path}}\),分别判定可不可能只走 \(S_{\mathrm{path}}\) 以内的边抵达,然后更新答案。注意把不封闭的 \(S_{\mathrm{path}}\) 排除掉。

正解

权值有 \(5! = 120\) 种时,封闭的 \(S_{\mathrm{path}}\) 有多少种呢?打表发现只有 \(156\) 种。那么把性质 G 的做法套用过来即可。

如何打表?随机一个子集,然后把它填充到封闭。重复 \(10^5\) 次。

*P10190 [USACO24FEB] Target Practice II S

容易发现,矩形的右上角要分给 \(s_i > 0\) 的奶牛,右下角要分给 \(s_i < 0\) 的奶牛。

考虑二分距离,发现难以 check,所以分别二分两个端点。check 是容易的,但是需要知道矩形的左上角和左下角分别分给哪种奶牛。发现越靠下的点越要给 \(s_i < 0\) 的奶牛,越靠上的点越要给 \(s_i > 0\) 的奶牛。按这个原则分配即可。

*CF1993D Med-imize

考虑二分答案。设当前要 check 中位数 \(m\)。令 \(b_i = [a_i \ge m]\),则答案 \(\ge m\) 当且仅当有一种操作序列满足剩下的 \(1\) 严格多于剩下的 \(0\)

那么怎么样求出最多剩下多少 \(1\) 呢?设最后剩下 \(l\) 个下标 \(r_i = i\)。那么可以将 \(r\) 的后缀 \(+kx\) 获得另一种方案。注意到无论怎么加,\(r_i \equiv i \pmod k\)

那么可以 DP。设前 \(i\) 个最多剩下 \(f_i\)\(1\),则:

\[\begin{cases} f_i = b_i \quad &i = 0 \\ f_i = \max(f_{i - k}, b_i) \quad &k \mid i \\ f_i = \max(f_{i - 1} + b_i, [i > k] \cdot f_{i - k}) \quad &k \nmid i \end{cases} \]

*CF1288D Minimax Problem

设下标从 \(0\) 开始。

二分答案,设当前 check 的答案为 \(k\)。因为 \(m \le 8\),所以可以令 \(b_i = \sum \limits_{j = 0}^{m - 1} [a_{i, j} \ge m] \cdot 2^j\),发现不同的 \(b_i\) 只有 \(2^m \le 256\) 种,所以开桶记录每种 \(b_i\) 的位置。我们需要找到 \((i, j)\) 满足 \(b_i \lor b_j = 2^m - 1\),那么 \(O(4^m)\) 枚举一下即可。

*2025.10.29 模拟赛 T3

给定长度为 \(n\) 的数列 \(A\)。你可以交换 \(A_i\)\(A_j\),当且仅当 \(A_i \cap A_j = 0\)。给出做任意次操作后,字典序最小的数组。可以不做操作。

\(1 \le n \le 10^6\)\(1 \le A_i < 2^{20}\)

把所有满足 \(A_i \cap A_j = 0\)\((i, j)\) 连边。发现同一个连通块内的元素可以排序,连通块间的元素无法交换。用并查集维护可以做到 \(O(n^2)\)

问题在于边太多了。设 \(V = 2^{20}, y' = V - 1 - y\),考虑转化条件:\(x \cap y = 0 \iff x \subseteq y'\)。所以把 \(A_i\)\(V - 1 - A_i\) 连无向边,\(V - 1 - A_i\) 和它的子集连有向边。在这个图上跑 SCC 即可。

连边时不需要枚举子集,只需要去掉一位然后连边即可。并且连无向边时不要直接把 \(A_i\)\(V - 1 - A_i\) 连边,要连 \(i + V \leftrightarrow V - 1 - A_i\)\(A_i \to i + V\)

*AGC018C Coins

错解

先令所有人选择自己的最大值,然后按最大值与次大值的差值升序排序。按顺序枚举,如果当前选择的超了人数,那就改选次大值。对于次大值和最小值重复以上操作。

为什么不对?在有两个重复的次大值时,并不知道选哪个更优,所以这样贪心是错误的。

正解

令所有人都选择金币,设当前的答案为 \(S\)。若第 \(i\) 个人改选银币,则 \(S \gets S + B_i - A_i\),铜币同理。

于是设 \(P_i = B_i - A_i, Q_i = C_i - A_i\)。现在相当于在所有 \((P_i, Q_i)\) 当中选 \(Y\)\(P_i\)\(Z\)\(Q_i\)。这是经典贪心。把二元组按 \(P_i - Q_i\) 降序排序,则一定能找到一个分界点 \(k\),使得选 \(P_i\) 的下标都 \(\le k\),选 \(Q_i\) 的下标都 \(> k\)

那么预处理:前 \(i\) 个中前 \(Y\) 大的 \(P_i\) 的和,与后 \(i\) 个中前 \(Z\) 大的 \(Q_i\) 的和,使用优先队列可以做到 \(O(n \log n)\)。然后枚举分界点并更新答案即可。

*CF1209E1 Rotate Columns (easy version)

转化一下操作:

  1. 任意旋转每一列。
  2. 在每一行中选择一个数,最大化它们的和。

那么可以 DP。令 \(f_{i, S}\) 表示:考虑前 \(i\) 列,第 \(j\) 行(\(j \in S\))选出了一个数时的答案。随便转移即可。

*P14362 [CSP-S 2025] 道路修复 / road(民间数据)

看这个标题应该就知道我啥时候补出来的了。

48 pts

\(O(2^k)\) 枚举每个额外点选不选,然后把这些边加进去跑 Kruskal。边数为 \(E = m + nk\),故总时间复杂度为 \(O(2^k E \log E)\)

80 pts

注意到原有的边不用全部保留,只需要保留最小生成树即可。\(O(2^k nk \log nk + m \log m)\)

100 pts

与 P10455 类似。可以提前把每个额外点的边排好序。每次枚举选哪些额外点时,不需要先把所有边加进去再排序,可以直接归并。\(O(2^k nk + m \log m + kn \log n)\)

*CF1834E MEX of LCM

发现答案 \(\le n^2 + 1\),那么只需要维护 \(\le n^2 + 1\)\(\operatorname{lcm}\)

从前往后扫右端点,拿 set 维护一下。每次把上次的 \(\operatorname{lcm}\)\(a_i\) 更新一下,然后全扔到一个大 set 即可。

CF1797E Li Hua and Array

与 CF992E 类似,发现每个数最多被修改 \(\log\) 次。那么每次暴力修改叶子,如果当前区间全部是 \(1\),那就跳过。

如何合并区间?记录区间会变成哪个数 \(t\) 以及修改的次数 \(c\) 以及长度 \(s\)。设左右区间的信息分别为 \((t_l, c_l, s_l)\)\((t_r, c_r, s_r)\),当前区间的信息为 \((t, c, s)\),合并时:

  • \(s \gets s_l + s_r\)
  • \(t \gets F(t_l, t_r)\),其中 \(F(x, y)\) 表示 \(x, y\) 可以变成的同一个数的最大值。
  • \(c \gets s_l \cdot L(t_l, t_r) + s_r \cdot R(t_l, t_r) + c_l + c_r\),其中 \(L(x, y), R(x, y)\) 分别表示 \(x, y\) 变成 \(F(x, y)\) 的过程中,\(x, y\) 做了多少次操作。

*CF2061F1 Kevin and Binary String (Easy Version)

错解

把 ARC205C 的做法搬到这里。因为 1 的相对位置不变,所以把每个 1 算出它要去的下标。然后把向左的 1 的连续段拿出来统一挪到最左边,向右的 1 的连续段拿出来统一挪到最右边。

但是没想清楚如何实现,导致没做出来。

正解

考虑增量构造。假设 \(s[1, i] = t[1, i]\)\(s_{i + 1} \neq t_{i + 1}\),那么把 \(t_{i + 1}\) 所在的连续段的右端点求出来,设为 \(r_1\)。然后求出最小的 \(r_2\),使得 \(\sum_{i = l}^{r_2} [s_i = t_l] = r_1 - l + 1\)。把 \(s[l, r_2]\) 挪到 \(s[l, r_1]\),然后继续往后构造即可。

*P14460 【MX-S10-T1】『FeOI-4』寻雾启示

错解

贪心。

90 pts

DP,设 \(f_i\) 表示到达第 \(i\) 格并回到起点的最短时间。转移为:

\[f_i = \min_{j < i} (\max(f_j, i \cdot k) + t_2 \cdot j + t_1 \cdot (i - j) + t_2 \cdot i) \]

为什么是 \(\max(f_j, i \cdot k)\)?因为发现生成 \(i\) 个铁需要 \(i \cdot k\) 的时间,所以在这个时间后一定能拿到 \(i\) 个铁。

100 pts

把转移式子变形:

\[f_i = \min_{j < i} (\max(f_j, i \cdot k) + (t_2 - t_1) \cdot j + (t_2 + t_1) \cdot i) \]

值域线段树维护 \(f_i \to f_i + (t_2 - t_1) \cdot i\) 的映射。查询 \([i \cdot k, V]\) 的最小值与最大的 \(\le i \cdot k\) 的下标,转移即可。\(V \le (k + t_1) \cdot m \le 2 \cdot 10^{14}\),需要动态开点线段树。

posted @ 2025-10-15 13:01  David9006  阅读(11)  评论(0)    收藏  举报