VII 2026.6.13 (10)
全面学习 Jonas. 本篇内的内容……。
\(\text{(I)}\) [CodeForces 1325 C] Ehab and Path-etic MEXs
已知一棵 \(n\) 个结点的树,需要给每条边分配一个 \(0\) 到 \(n-2\) 之间的整数,且所有边上的数字互不相同。定义 \(MEX(u,v)\) 为从结点 \(u\) 到 \(v\) 的唯一简单路径上未出现的最小非负整数。要求最小化所有结点对 \((u,v)\) 的 \(MEX(u,v)\) 的最大值。输出任意一种满足条件的边标号(按输入顺序)。
数据范围:\(2 \le n \le 10^5\)。
Q: 本题要求给一棵树的所有边分配 \(0\) 到 \(n-2\) 的权值,使得所有路径的 \(\mathrm{MEX}\) 的最大值最小。核心思路是什么?
A: 关键在于观察:若存在一个度数 \(\ge 3\) 的结点,则与其相连的三条边可分别标号为 \(0,1,2\),这样任何包含这三条边中任意两条的路径其 \(\mathrm{MEX}\) 至少为 \(3\);其余边只需分配剩余权值即可。若不存在这样的结点,则树是一条链,按顺序分配 \(0,1,\dots,n-2\) 即可。
Q: 为什么当存在度数 \(\ge 3\) 的结点时,将与之相连的三条边标为 \(0,1,2\) 是最优的?
A: 考虑以该结点为端点的三条边,若某条路径同时包含其中两条,则路径上已出现 \(0,1,2\),其 \(\mathrm{MEX}\ge 3\);若只包含一条或不包含,则 \(\mathrm{MEX}\) 由路径上实际出现的权值决定,不会超过已分配的最大权值。因此全局最大 \(\mathrm{MEX}\) 被控制在 \(3\) 以内,这是最优可能。
Q: 代码中如何具体实现这一构造?
A: 首先寻找度数 \(>2\) 的结点 \(p\):
- 若不存在,直接输出 \(0,1,\dots,n-2\)。
- 若存在,遍历每条边 \((u_i,v_i)\):
- 若边连接 \(p\) 与其第 \(k\) 个邻居(\(k=0,1,2\)),则分配权值 \(k\)。
- 否则分配递增的权值(从 \(3\) 开始)。
这样保证了 \(p\) 的三条邻边权值为 \(0,1,2\),其余边权值互不冲突。
Q: 如何证明该构造的正确性?
A: 设 \(T\) 为原树,\(w(e)\) 为边权。
- 对任意路径 \(P\),若 \(P\) 包含两条与 \(p\) 相连的边,则 \(\{0,1,2\}\subseteq w(P)\),故 \(\mathrm{MEX}(P)\ge 3\)。
- 若 \(P\) 不包含两条此类边,则 \(w(P)\) 中缺失 \(0,1,2\) 中的至少一个,故 \(\mathrm{MEX}(P)\le 2\)。
因此全局最大 \(\mathrm{MEX}=2\)(当存在度数 \(\ge 3\) 结点时)或 \(n-2\)(链的情况),达到最优。
\(\text{(II)}\) [CodeForces 1295 B] Infinite Prefixes
已知二进制字符串 \(s\)(长度 \(n\)),构造无限字符串 \(t = ssss\ldots\)(\(s\) 无限重复)。定义字符串 \(q\) 的平衡值为 \(\text{cnt}_0(q) - \text{cnt}_1(q)\)(\(0\) 的个数减 \(1\) 的个数)。求 \(t\) 的所有前缀(包括空前缀)中平衡值等于给定整数 \(x\) 的个数。如果有无穷多个,输出 \(-1\)。
数据范围:\(1 \le T \le 100\),\(1 \le n \le 10^5\),\(\sum n \le 10^5\),\(-10^9 \le x \le 10^9\),\(s_i \in \{0,1\}\)。
Q: 本题要求统计无限字符串 \(t = ssss\ldots\) 的前缀中平衡值等于 \(x\) 的个数,平衡值定义为 \(\text{cnt}_0 - \text{cnt}_1\)。核心思路是什么?
A: 先计算 \(s\) 的前缀平衡值数组 \(a[i] = \sum_{j=1}^i (s_j = \text{'0'} ? 1 : -1)\),并记整个 \(s\) 的平衡值为 \(p = a[n]\)。对于 \(t\) 的第 \(k\) 个完整周期后的位置,其平衡值可表示为 \(k \cdot p + a[r]\),其中 \(r\) 是当前周期内的偏移量。问题转化为求解方程 \(k \cdot p + a[r] = x\) 的非负整数解 \((k, r)\) 的数量。
Q: 当 \(p = 0\) 时为什么可能有无穷多解?如何判断?
A: 若 \(p = 0\),则每个完整周期后平衡值不变。此时若 \(s\) 的某个前缀平衡值等于 \(x\),则 \(t\) 中所有对应的前缀(每隔 \(n\) 个字符)都会满足,导致无穷多解。代码中通过检查是否存在 \(a[i] = x\)(\(0 \le i \le n\))来判断:若存在则输出 \(-1\),否则输出 \(0\)。
Q: 当 \(p \neq 0\) 时如何统计解的数量?
A: 对于每个偏移量 \(r\)(\(1 \le r \le n\)),需要存在非负整数 \(k\) 使得 \(k \cdot p + a[r] = x\),即 \(k = \frac{x - a[r]}{p}\)。条件为:
- \((x - a[r])\) 能被 \(p\) 整除;
- \(k \ge 0\),即 \((x - a[r])\) 与 \(p\) 同号(或 \(x - a[r] = 0\))。
此外,空前缀(长度为 \(0\))的平衡值为 \(0\),故若 \(x = 0\) 则需额外计数一次。
Q: 代码中 ans = (x == 0) 的作用是什么?
A: 这是统计空前缀的情况。空前缀的平衡值恒为 \(0\),因此当 \(x = 0\) 时,空前缀本身就是一个解,初始化 ans = 1;否则初始化为 \(0\)。后续再枚举每个周期内的偏移量 \(r\) 进行累加。
Q: 如何证明该算法的正确性?
A: 由 \(t\) 的构造可知,其任意前缀可表示为 \(k\) 个完整 \(s\) 加上 \(s\) 的一个前缀,平衡值即为 \(k \cdot p + a[r]\)。算法穷举所有可能的 \(r\),并检查是否存在合法的 \(k\),覆盖了所有可能的前缀。边界条件(空前缀、\(p=0\) 的特殊情况)均已正确处理,故结果正确。
\(\text{(III)}\) [CodeForces 1283 D] Christmas Trees
已知数轴上 \(n\) 个互异整数点(圣诞树位置)\(x_1, x_2, \dots, x_n\)。需要放置 \(m\) 个互异整数点(人的位置)\(y_1, y_2, \dots, y_m\),且与所有 \(x_i\) 不重复。令 \(d_j = \min\limits_{i=1}^n |y_j - x_i|\),求 \(\sum\limits_{j=1}^m d_j\) 的最小值,并输出任意一组达到该最小值的 \(y_j\)。
数据范围:\(1 \le n, m \le 2 \times 10^5\),\(|x_i| \le 10^9\)。
Q: 本题要求在数轴上放置 \(m\) 个人,使得每个人到最近的圣诞树的距离之和最小。核心思路是什么?
A: 采用多源 BFS 的贪心策略:从所有圣诞树同时开始向外扩散,每次优先占据距离当前树最近且未被占用的整数点。这样能保证每一步选择的都是当前全局距离最小的空位,从而得到最优解。
Q: 为什么多源 BFS 能保证得到最小总距离?
A: 因为 BFS 按距离层次遍历,先访问到的点一定具有更小的距离值。将前 \(m\) 个被访问到的空位分配给人类,等价于每次选择当前可用的最小距离点,这是典型的贪心最优策略。
Q: 代码中如何实现“向外扩散”并避免重复占用?
A: 使用队列 q 存储待扩展的点,map<int,int> dis 记录每个点的距离(也是是否被占用)。初始将所有圣诞树入队,距离设为 \(0\)。每次取出队首 \(u\),将其距离加入答案(若 \(u\) 不是树),并尝试扩展 \(u-1\) 和 \(u+1\):若新点未被占用(!dis.count(nx)),则设置距离为 dis[u]+1 并入队。
Q: 为什么 if (dis[u]) ansx.push_back(u); 中要排除距离为 \(0\) 的点?
A: 因为距离为 \(0\) 的点正是圣诞树本身,题目要求人的位置不能与树重合,所以只将距离大于 \(0\) 的点(即非树点)加入答案数组 ansx。
Q: 算法的时间复杂度如何?能否处理 \(|x_i| \le 10^9\) 的大坐标?
A: 每个点最多被访问一次,每次操作是 \(O(1)\) 的,总复杂度 \(O((n+m) \log (n+m))\)(主要来自 map 的插入和查询)。虽然坐标范围很大,但实际访问的点数只有 \(O(n+m)\) 个,因此可以高效处理。
\(\text{(IV)}\) [CodeForces 1286 A] Garland
已知长度为 \(n\) 的序列 \(p_1, p_2, \dots, p_n\),其中 \(p_i\) 为 \(0\) 或 \(1\) 到 \(n\) 的互不相同的整数。值为 \(0\) 的位置需要填入尚未出现的 \(1\) 到 \(n\) 中的数(每个数恰好填一次)。定义复杂度为相邻两个数奇偶性不同的对数。求最小可能复杂度。
数据范围:\(1 \le n \le 100\)。
Q: 本题要求将序列中的 \(0\) 替换为 \(1\) 到 \(n\) 中未出现的数字,使得相邻两数奇偶性不同的对数最少。核心思路是什么?
A: 采用动态规划,按位置依次决策每个 \(0\) 应填入奇数还是偶数,同时记录已使用的奇数个数,以最小化相邻奇偶性变化次数。
Q: 动态规划的状态如何设计?为什么这样设计?
A: 设 \(\text{dp}[i][j][k]\) 表示前 \(i\) 个位置中,已填入 \(j\) 个奇数,且第 \(i\) 个数的奇偶性为 \(k\)(\(k=0\) 为偶数,\(k=1\) 为奇数)的最小复杂度。
- 需要记录奇数个数,因为 \(1\) 到 \(n\) 中奇数的总数固定为 \(\lceil n/2 \rceil\),必须恰好用完。
- 记录最后一个数的奇偶性是为了计算相邻变化。
Q: 状态转移方程如何推导?
A: 分三种情况讨论:
-
若 \(p_i = 0\)(可填奇或偶):
- 填偶数:\(j\) 不变,复杂度增加 \(\mathbb{I}(k_{\text{prev}}=1)\),即
\(\text{dp}[i][j][0] = \min(\text{dp}[i-1][j][0],\ \text{dp}[i-1][j][1] + 1)\)。 - 填奇数:\(j\) 增加 \(1\),复杂度增加 \(\mathbb{I}(k_{\text{prev}}=0)\),即
\(\text{dp}[i][j][1] = \min(\text{dp}[i-1][j-1][1],\ \text{dp}[i-1][j-1][0] + 1)\)。
- 填偶数:\(j\) 不变,复杂度增加 \(\mathbb{I}(k_{\text{prev}}=1)\),即
-
若 \(p_i\) 为奇数:只能填奇数,转移同上填奇数的情形。
-
若 \(p_i\) 为偶数:只能填偶数,转移同上填偶数的情形。
Q: 初始化和边界条件如何处理?
A: 初始化 \(\text{dp}[0][0][0] = \text{dp}[0][0][1] = 0\),其余为无穷大。
- 前 \(0\) 个数无相邻变化,代价为 \(0\)。
- 限制 \(j \le \min(i, \lceil n/2 \rceil)\),因为奇数最多 \(\lceil n/2 \rceil\) 个。
Q: 最终答案如何得出?
A: 答案为 \(\min(\text{dp}[n][\lceil n/2 \rceil][0],\ \text{dp}[n][\lceil n/2 \rceil][1])\)。
- 必须使用恰好 \(\lceil n/2 \rceil\) 个奇数。
- 取最后一位为奇数或偶数的较小值。
\(\text{(V)}\) [CodeForces 1270 D] Strange Device
已知隐藏数组 \(a_1, a_2, \dots, a_n\),所有元素互不相同。你可以询问:选择 \(k\) 个不同的下标 \(x_1, x_2, \dots, x_k\),设备会返回这些位置上元素中按升序排第 \(m\) 个的元素的下标和值。你知道 \(k\) 但不知道 \(m\)(\(1 \le m \le k\)),且可以询问最多 \(n\) 次。试确定 \(m\)。
数据范围:\(1 \le k < n \le 500\),\(0 \le a_i \le 10^9\),所有 \(a_i\) 互异。交互。
Q: 本题要求通过最多 \(n\) 次询问确定隐藏数组中第 \(m\) 小的位置,已知每次询问返回 \(k\) 个不同下标中第 \(m\) 小元素的值和下标。核心思路是什么?
A: 利用 \(k+1\) 次询问覆盖所有可能的排除情况,通过统计返回值的出现频率推断 \(m\)。具体地,对 \(k+1\) 个不同的下标集合,每次排除其中一个下标,询问剩余 \(k\) 个下标的第 \(m\) 小值。由于 \(m\) 固定,返回值的分布会呈现特定规律,出现次数最多的次数即为 \(m\)。
Q: 为什么通过 \(k+1\) 次询问就能确定 \(m\)?
A: 考虑 \(k+1\) 个互不相同的元素,排序后为 \(b_1 < b_2 < \dots < b_{k+1}\)。每次排除第 \(i\) 个元素后,剩余 \(k\) 个元素的第 \(m\) 小值为:
- 若 \(i \le m\),则第 \(m\) 小是 \(b_{m+1}\)(因排除了较小的 \(b_i\));
- 若 \(i > m\),则第 \(m\) 小是 \(b_m\)(因 \(b_m\) 仍在剩余集合中)。
因此,\(b_m\) 会出现 \(k+1-m\) 次,\(b_{m+1}\) 会出现 \(m\) 次。由于 \(m \le k\),\(b_{m+1}\) 的出现次数 \(m\) 即为所求。
Q: 代码中如何通过返回值统计得到 \(m\)?
A: 进行 \(k+1\) 次询问,每次排除一个不同的下标(从 \(1\) 到 \(k+1\)),记录每次返回的“值” \(y\)。用 map<int,int> mp 统计每个 \(y\) 的出现次数。最终,出现次数最多的 \(y\) 对应的次数即为 \(m\)。代码中取最大的 \(y\)(即 \(b_{m+1}\))对应的次数,因为 \(b_{m+1}\) 的出现次数恰好是 \(m\)。
Q: 为什么选择排除 \(k+1\) 个下标而非其他方式?
A: 因为 \(k+1\) 是能形成完整对称性的最小数量。若少于 \(k+1\) 次,无法覆盖所有可能的排除情况,导致统计规律不完整;若多于 \(k+1\) 次,虽可行但浪费询问次数(题目限制 \(n\) 次,而 \(k+1 \le n\) 恒成立)。
Q: 如何证明该策略的正确性?
A: 由上述分析,对于固定的 \(m\),返回值中 \(b_{m+1}\) 恰好出现 \(m\) 次,且这是唯一出现次数为 \(m\) 的值。因此,统计所有返回值的频率,最大值对应的次数必为 \(m\)。该策略仅需 \(k+1\) 次询问,满足题目要求。
\(\text{(VI)}\) [CodeForces 1283 F] DIY Garland
已知正整数 \(n\) 及序列 \(a_1, a_2, \dots, a_{n-1}\),其中 \(a_i\) 表示按重要性降序排列的第 \(i\) 条边的主灯编号。每盏灯 \(i\) 的亮度为 \(2^i\)。一棵有 \(n\) 个节点的树,根节点连接电源;每条边的重要性定义为:若切断该边,则被分离出的子树中所有节点的亮度之和。请根据序列 \(a_1,\dots,a_{n-1}\) 还原树的结构,输出根节点编号及所有边;若无法还原则输出 \(-1\)。
数据范围:\(2 \le n \le 2 \times 10^5\)。
Q: 本题要求根据按重要性降序排列的边主灯编号序列 \(a_1, a_2, \dots, a_{n-1}\) 还原树的结构。核心思路是什么?
A: 关键观察是每条边对应一个父节点,且所有非根节点都会作为子节点出现一次。根节点为 \(a_1\)(最重要的边连接根节点)。通过统计每个父节点的子节点需求数,并利用优先队列从小到大分配子节点,可确保每条边正确连接,同时满足重要性顺序。
Q: 为什么根节点是 \(a_1\)?
A: 因为 \(a_1\) 是最重要的边,其主灯编号应为根节点(或连接根节点的边)。代码直接输出 \(a_1\) 作为根节点,这隐含了 \(a_1\) 是根节点。
Q: 如何确定每个父节点的子节点?
A: 使用数组 \(p\) 统计每个节点作为父节点出现的次数(即需要连接的子节点数)。初始时,所有未在 \(a\) 中出现的节点(即 \(p[i]=0\))是潜在的子节点,存入小顶堆优先队列。从后向前遍历 \(a\),每次取出最小的可用子节点 \(u\),连接 \(a_i\) 和 \(u\),并将 \(a_i\) 的剩余子节点计数减一;当 \(a_i\) 的计数归零时,它成为新的可用子节点。
Q: 为什么从后向前遍历 \(a\)?
A: 因为 \(a\) 按重要性降序排列,从后向前处理相当于先处理重要性较低的边,最后处理最重要的边(连接根节点的边),这样可以确保所有子节点在需要时可用,避免冲突。
Q: 如何证明该构造的正确性?
A: 每个父节点 \(a_i\) 必须有恰好 \(p[a_i]\) 个子节点。通过优先队列保证子节点从小到大分配,避免了重复或遗漏。由于 \(a\) 的长度为 \(n-1\),且每个非根节点恰好作为子节点一次,最终能构建出唯一的树结构。
\(\text{(VII)}\) [USACO 2018 DECEMBER] Fine Dining G
已知无向连通图 \(G=(V,E)\),其中顶点集 \(V=\{1,2,\dots,N\}\),边集 \(E\) 含 \(M\) 条边 \((a_i,b_i,t_i)\) 表示顶点 \(a_i,b_i\) 间通行时间为 \(t_i\)。设 \(d(u,v)\) 为顶点 \(u\) 到 \(v\) 的最短路径长度。现有 \(K\) 个干草捆,第 \(j\) 个位于顶点 \(p_j\),美味值为 \(y_j\)。对于 \(i=1,2,\dots,N-1\),判断是否存在 \(j\in\{1,\dots,K\}\) 使得 \(d(i,p_j)+d(p_j,N)\le d(i,N)+y_j\),成立则输出 \(1\),否则输出 \(0\)。
数据范围:\(2\le N\le5\times10^4\),\(1\le M\le10^5\),\(1\le K\le N\),\(t_i\le10^4\),\(y_j\le10^9\)。
Q: 如何将判断条件 \(d(i,p_j) + d(p_j,N) \le d(i,N) + y_j\) 转化为最短路问题?
A: 将不等式变形为 \(d(i,N) - d(i,p_j) - d(p_j,N) \ge -y_j\),即 \(d(i,N) \ge d(i,p_j) + (d(p_j,N) - y_j)\)。这等价于存在干草捆 \(j\) 使得从 \(i\) 到 \(N\) 的普通最短路 \(d(i,N)\) 不小于经过 \(p_j\) 的“修正路径”长度 \(d(i,p_j) + (d(p_j,N) - y_j)\)。
Q: 如何通过建图实现上述转化?
A: 将原图每个节点 \(u\) 拆分为 \(u\) 和 \(u+N\) 两个副本。原图的每条边 \((u,v,t)\) 在两个副本中均添加双向边(权重 \(t\))。对每个干草捆 \((p_j,y_j)\),添加有向边 \(p_j \to p_j+N\),权重为 \(-y_j\)。这样,从 \(N\) 出发到 \(i+N\) 的最短路径 \(dis[i+N]\) 即为 \(\min_j \left(d(N,p_j) - y_j + d(p_j,i)\right)\),恰好对应修正路径的最小值。
Q: 为什么从 \(N\) 出发跑最短路能得到所需结果?
A: 因为 \(dis[i]\)(原图最短路)等于 \(d(i,N)\),而 \(dis[i+N]\) 是经过至少一个干草捆的修正最短路。若 \(dis[i] \ge dis[i+N]\),则存在 \(j\) 满足 \(d(i,N) \ge d(i,p_j) + d(p_j,N) - y_j\),即原不等式成立。
Q: 代码中如何处理多个干草捆?
A: 对每个干草捆 \((p,x)\),添加边 \(p \to p+N\) 权重 \(-x\)。任何从 \(N\) 到 \(i+N\) 的路径都会经过某个 \(p_j\) 并减去 \(y_j\),自动取最小值,无需显式枚举所有干草捆。
Q: 为什么使用 SPFA 而非 Dijkstra?
A: 因为添加了负权边(\(-y_j\)),Dijkstra 无法处理负权。SPFA 可处理负权且无负环风险(负边仅从 \(p_j\) 指向 \(p_j+N\),不形成环)。
Q: 最终如何输出结果?
A: 对每个 \(i=1,\dots,N-1\),若 \(dis[i] \ge dis[i+N]\) 则输出 \(1\),否则输出 \(0\)。这直接对应原不等式是否成立。
\(\text{(VIII)}\) [AtCoder Regular Contest 170 C] Prefix Mex Sequence
已知非负整数列 \(X\) 的 \(\mathrm{mex}(X)\) 定义为 \(X\) 中未出现的最小非负整数。给定长度为 \(N\) 的 \(0\)-\(1\) 数列 \(S=(S_1,S_2,\dots,S_N)\),求满足以下条件的长度为 \(N\) 的数列 \(A=(A_1,A_2,\dots,A_N)\)(\(0\le A_i\le M\))的个数,结果对 \(998244353\) 取模:对任意 \(1\le i\le N\),若 \(S_i=1\),则 \(A_i=\mathrm{mex}((A_1,A_2,\dots,A_{i-1}))\);若 \(S_i=0\),则 \(A_i\neq\mathrm{mex}((A_1,A_2,\dots,A_{i-1}))\)。
数据范围:\(1\le N\le 5000\),\(0\le M\le 10^9\),\(S_i\in\{0,1\}\)。
Q: 为什么当 \(M \ge N\) 时,答案可以简化为乘积形式?
A: 当 \(M \ge N\) 时,所有可能的 mex 值均在 \(0\) 到 \(N\) 之间,而 \(M\) 足够大,每个 \(S_i=0\) 的位置均可自由选择 \(M\) 种不同的数(不受 mex 范围限制),\(S_i=1\) 的位置必须严格等于前一个的 mex,仅有 \(1\) 种选择。因此总方案数为 \(\prod_{i=1}^N (S_i=0 \,?\, M : 1)\),对应代码中直接计算的乘积。
Q: 当 \(M < N\) 时,如何设计动态规划状态?
A: 设 \(\text{dp}[i][j]\) 表示前 \(i\) 个元素构造完成后,当前 mex 值为 \(j\) 的方案数(其中 \(j \in [1, m]\),\(m = M+1\) 用于覆盖所有可能的 mex 值)。此设计因 mex 最多为 \(M+1\)(元素仅能选 \(0\) 到 \(M\)),可枚举有限状态。
Q: 当 \(S_i=1\) 时,状态转移方程是什么?为什么?
A: 此时 \(A_i\) 必须等于前 \(i-1\) 个元素的 mex,即 \(j-1\)(对应 \(\text{dp}[i-1][j-1]\))。加入 \(A_i = j-1\) 后,新的 mex 变为 \(j\),故转移方程为:
Q: 当 \(S_i=0\) 时,状态转移方程是什么?为什么?
A: 此时 \(A_i\) 不能等于前 \(i-1\) 个元素的 mex \(j\),分两种情况:
- 前 \(i-1\) 个的 mex 已是 \(j\),则 \(A_i\) 可选已出现的 \(j\) 个数(\(0\) 到 \(j-1\)),新 mex 仍为 \(j\),贡献 \(\text{dp}[i-1][j] \times j\);
- 前 \(i-1\) 个的 mex 是 \(j-1\),则 \(A_i\) 可选比 \(j-1\) 大的数(共 \(m-j\) 种,因总共有 \(m\) 个数 \(0\) 到 \(m-1\),排除 \(j-1\) 后有 \(m-(j-1)-1 = m-j\) 种),新 mex 变为 \(j\),贡献 \(\text{dp}[i-1][j-1] \times (m-j)\)。
综合得转移方程:
Q: 最终答案如何计算?
A: 所有前 \(N\) 个元素的 mex 值 \(j\)(\(1 \le j \le m\))的方案数之和,即:
Q: 为什么要将 \(m\) 设置为 \(M+1\)?
A: 当 \(M < N\) 时,mex 的最大可能值为 \(M+1\)(例如 \(0\) 到 \(M\) 全部出现后,下一个 mex 是 \(M+1\))。用 \(m = M+1\) 可覆盖所有可能的 mex 值,简化边界处理。
\(\text{(IX)}\) [CodeForces 1698 E] PermutationForces II
已知长度为 \(n\) 的排列 \(a=(a_1,a_2,\dots,a_n)\),正整数 \(s\)。定义操作:第 \(i\) 步(\(1\le i\le n\))可选择下标 \(x,y\) 满足 \(i\le x\le y\le \min(i+s,n)\),交换 \(a_x\) 与 \(a_y\)(允许 \(x=y\),即不交换)。给定目标序列 \(b=(b_1,b_2,\dots,b_n)\),其中部分 \(b_i=-1\)(未知),其余为 \(1\sim n\) 的互异整数。求将每个 \(-1\) 替换为 \(1\sim n\) 中互异整数的方案数,使得存在合法操作序列可将 \(a\) 变为 \(b\)。答案对 \(998\,244\,353\) 取模。
数据范围:\(1\le t\le 1000\),\(\sum n\le 2\times 10^5\),\(1\le s\le n\)。
Q: 本题的核心思路是什么?
A: 首先判断是否存在合法操作序列将排列 \(a\) 变为 \(b\)。关键在于:对于每个值 \(i\),如果它在 \(b\) 中已知(即 \(b\) 中某位置为 \(i\)),则它在 \(a\) 中的位置必须满足 \(a[\operatorname{pos}_b(i)] - i \le s\),否则不可能。其次,填充未知位置时,只需按值从小到大的顺序,每次从当前可移动范围内未使用的值中任选一个即可,方案数相乘。
Q: 为什么条件 \(a[\operatorname{pos}_b(i)] - i \le s\) 是必要的?
A: 操作限制:在第 \(i\) 步只能交换区间 \([i, i+s]\) 内的元素。这意味着值 \(i\) 最早可以在第 \(i\) 步被处理,且最多只能向右移动 \(s\) 步。若 \(a[\operatorname{pos}_b(i)]\) 过大(超过 \(i+s\)),则该位置上的值会阻碍 \(i\) 的移动,导致无法完成变换。
Q: 如何计算填充未知位置的方案数?
A: 从左到右处理值 \(i=1\) 到 \(n\)。维护指针 \(p\) 覆盖区间 \([i, i+s]\),统计其中在 \(b\) 中未出现的值(即 \(\operatorname{vis}[\operatorname{pos}_a(p)]=0\) 的值 \(p\))。若值 \(i\) 在 \(b\) 中未知,则将其分配给任意一个当前可用的未使用值,方案数乘上可用值个数 \(\mathit{cnt}\),随后 \(\mathit{cnt}\) 减一。
Q: 为什么这样计数正确?
A: 由于操作的限制,值 \(i\) 只能从 \(a\) 中移动到 \(b\) 中位置 \(\ge i\) 的地方。按 \(i\) 递增顺序分配,保证了每个值都有足够的空间移动,且可用值的选择互不影响。
Q: 代码中如何实现?
A: 首先用数组 \(c\) 记录每个值在 \(a\) 中的位置,\(d\) 记录每个值在 \(b\) 中的位置。检查条件 \(a[d[i]] - i \le s\)。然后计算方案数时,用指针 \(p\) 滑动窗口统计可用值,遇到 \(d[i]=-1\) 时乘 \(\mathit{cnt}\) 并更新。
\(\text{(X)}\) [CodeForces 1290 C] Prefix Enlightenment
已知 \(n\) 盏灯,初始状态为 \(s_1,s_2,\dots,s_n\in\{0,1\}\)。给定 \(k\) 个子集 \(A_1,\dots,A_k\subseteq\{1,2,\dots,n\}\),满足对任意 \(1\le i_1<i_2<i_3\le k\) 有 \(A_{i_1}\cap A_{i_2}\cap A_{i_3}=\varnothing\)。每次操作可选择某个 \(A_j\) 并翻转其中所有灯的状态。保证存在操作序列可使所有灯均为 \(1\)。对每个 \(i=1,2,\dots,n\),求最少操作次数 \(m_i\),使得前 \(i\) 盏灯全亮(其余灯状态任意)。输出 \(m_i\)。
数据范围:\(1\le n,k\le 3\times10^5\)。
Q: 为什么题目中“任意三个子集交集为空”的条件至关重要?
A: 该条件保证每个元素最多属于两个子集,从而将问题转化为每个灯最多受两个操作影响。这使得我们可以用并查集维护子集间的约束关系,避免复杂的组合爆炸。
Q: 如何用并查集建模子集间的依赖关系?
A: 每个子集视为一个节点。若两个子集共享同一个元素,则它们必须被合并(因为该元素的状态由这两个子集共同决定)。由于每个元素最多连接两个子集,并查集形成森林结构,每个连通分量代表一组相互关联的子集。
Q: tag 数组的作用是什么?
A: tag[x] 记录节点 x 相对于其父节点的翻转状态。例如,若 tag[x] = 1,则表示子集 x 需要被翻转才能使当前灯亮起。路径压缩时同步更新 tag,确保查询时能正确反映实际翻转状态。
Q: c1 和 c2 的含义是什么?
A:
c1[x]:在并查集x中,选择翻转该子集的代价(初始为1,表示翻转一次)。c2[x]:标记该并查集是否包含两个冲突的子集(即两个子集共同影响同一盏灯)。若c2[x] = 1,则需特殊处理以避免重复计数。
Q: cal(i) 函数如何处理第 i 盏灯?
A: 分两种情况:
- 灯
i只属于一个子集(r[i] = 0):调整该子集的翻转状态,使灯i亮起。若当前状态与目标不符,则翻转该子集(tag[x] ^= 1),并更新代价c1[x] = -c1[x](表示撤销操作)。 - 灯
i属于两个子集(l[i]和r[i]):比较两个子集的代价,选择更优的翻转方式。若两子集的当前状态与目标不符,则选择代价较小的子集翻转,或根据c2标记处理冲突。
Q: 为什么在合并两个并查集时要比较 c1[x] 和 c1[y]?
A: 当两个子集共同影响一盏灯时,需选择翻转代价更小的子集。例如,若 c1[x] < c1[y] 且 c2[x] = 0,则翻转 x 更优;否则翻转 y。通过比较代价,确保每次操作都是局部最优。
Q: 最终答案如何逐步生成?
A: 对每个前缀 i(从 1 到 n),调用 cal(i) 更新最小操作次数。每次处理完第 i 盏灯后,输出当前累计的最小操作次数 ans,即为前 i 盏灯全亮的最少操作数。

浙公网安备 33010602011771号