MATH Day 07 Application & Practice

本周算法部分复习

线段树 (Segment Tree)

  • 基本操作:单点查询/修改;区间查询/修改;Lazy Tag (延迟标记)。
  • 变体与进阶:动态开点线段树;线段树合并/分裂;持久化线段树 (Persistent Segment Tree)。
  • 势能线段树 (Potential Segment Tree)
    • 常见应用:区间开根 (Range Sqrt)、区间取模 (Range Mod)。
    • Segment Tree Beats 类问题:涉及区间 \(min/max\) 修改的问题。
    • 复杂度势能分析:通过定义势能函数 \(\Phi\) 证明这类看似暴力的修改在均摊意义下是 \(O(\log n)\)\(O(\log^2 n)\) 的。
  • 矩阵 Tag (2x2 线段树):利用矩阵乘法的结合律来维护复杂的线性递推区间修改。
  • 标记永久化 (Tag Propagation Elimination):在不支持 pushdown 的场景(如可持久化或某些高维结构)中,将标记留在节点上,在查询路径上累加。

「 补充建议:关于势能分析 」

在处理区间开根号时,注意到 \(\lfloor \sqrt{x} \rfloor\) 下降极快。对于 \(10^9\) 范围内的数,最多 6 次操作就会变成 \(1\)\(0\)。因此可以通过维护区间最大值,若 \(max \le 1\) 则停止递归,从而保证复杂度。

李超线段树 (Li Chao Tree)

  • 简要:十分有用的数据结构,用于线性最值 DP 优化 (Convex Hull Trick 的动态变体)。
  • 原理:一次函数的“优势”性质。
  • 手段:标记永久化。
  • 核心方法:加入线段 \(f\)\([l, r]\) 上:\(gmax\)
    • \(f(mid) > f(mid)_{old}\),则:替换掉这一点上的线段。(此时处理了该点以上区间的更新,另外部分由被替换线段再更新)
    • Dominant Line (优势线段):在当前区间中点处值最大的线段。

「 补充建议:算法流程细节 」

插入新线段 \(f\) 时,对比当前节点的优势线段 \(g\)

  1. 如果 \(f\)\([l, r]\) 完全高于 \(g\),直接覆盖并返回。
  2. 如果 \(f\)\([l, r]\) 完全低于 \(g\),直接丢弃并返回。
  3. 如果相交:
    • \(f(mid) > g(mid)\),交换 \(f\)\(g\)(保持 \(g\) 为优势线段),然后将新的 \(f\) 递归下传到斜率更差的一侧。
    • \(f(mid) \le g(mid)\),将 \(f\) 递归下传到斜率更好的一侧。
      复杂度为严格 \(O(\log (\text{Value Range}))\)

DS 模板小练习

CF380C

  • 题目描述:一个括号序列 \(\{s_n\}\),长度为 \(n\)\(m\) 次询问,每次询问 \([l_i, r_i]\) 中最大合法括号子序列的长度。
  • :简单数据结构题,每个子区间维护段内未匹配的 ( (记作 \(a\)) 和 ) (记作 \(b\)),以及已匹配的长度 \(ans\)
  • 合并区间时统计
    • \(match = \min(a_{left}, b_{right})\)
    • \(ans_{new} = ans_{left} + ans_{right} + match \times 2\)
    • \(a_{new} = a_{left} + a_{right} - match\)
    • \(b_{new} = b_{left} + b_{right} - match\)

CF474F

  • 题目描述:维护 \([l, r]\) 内值为 \(\gcd_{i \in [l, r]} x_i\) 的数字个数。
  • :线段树维护 \(\gcd\)。离散化 + vector 维护区间内数字个数(或者在询问时同时统计该 \(\gcd\) 出现的次数)。
  • 数学原理:区间 \(\gcd\) 一定是区间内任意元素的约数。若某个数 \(x_i\) 等于区间 \(\gcd\),则它一定是该区间内的最小值(或之一)。
  • 进阶思路:由于 \(\gcd\) 具有幂等性且随区间增大单调不增,可以使用 ST 表在 \(O(n \log n)\) 预处理后 \(O(1)\) 查询区间 \(\gcd\),配合二分或 std::upper_bound/lower_bound 在 vector 中统计个数。

CF482B (Interesting Array)

题意:构建一个序列 \(a\),满足 \(m\) 条限制,形如 \(l, r, q \Rightarrow a_l \& \dots \& a_r = q\)。否则输出 NO。

:显然按位考虑。

  • 对于每一个比特位(bit),如果 \(q\) 的该位为 \(1\),则区间 \([l, r]\) 内所有数的该位都必须为 \(1\)。这等价于区间赋值(或区间加)。
  • 如果 \(q\) 的该位为 \(0\),则要求区间 \([l, r]\) 内至少有一个数的该位为 \(0\)。这等价于在构造完序列后,逐个验证区间的位与(AND)结果是否确实为 \(0\)

「 补充建议 」

  • 实现细节:可以使用差分数组来处理按位的区间赋值,复杂度为 \(O(30n)\)
  • 校验环节:构造完 \(a\) 序列后,建立一棵线段树维护区间 AND 值,对 \(m\) 条限制进行 \(O(\log n)\) 的区间查询。若查询结果不等于 \(q\),则输出 NO。总复杂度 \(O(30(n+m))\)

CF914D (Bash and a Tough Puzzle)

题意:给定 \(a_n\)\(m\) 次操作:
1 l r x:在 \(a_l \sim a_r\) 中修改至多一个数,使 \(\gcd(a_l \dots a_r) = x\) 的可行性。
2 i y:将 \(a_i\) 修改为 \(y\)
\(n \le 5 \times 10^5, m \le 4 \times 10^5, a_i, x, y \le 10^9\)

:核心难点在于操作 ①。

  • 注意到若除一个数以外所有数都被 \(x\) 整除,直接令此数为 \(x\) 可取得目标 \(\gcd\)
  • 考虑查询一个区间是否恰有 \(\le 1\) 个数不被 \(x\) 整除。
  • 线段树上二分即可。复杂度 \(O(n \log n \log V)\),其中 \(\log V\) 来自 pushup 时的 \(\gcd\) 计算。

「 补充建议:剪枝优化 」

  • 递归退出条件:在查询区间 \([l, r]\) 时,如果当前节点的区间 \(\gcd\)\(x\) 的倍数(即 tr[u].g % x == 0),说明该子树内所有数都能被 \(x\) 整除,直接返回 \(0\)(错误个数)。
  • 错误计数:如果当前节点是叶子节点且 tr[u].g % x != 0,返回 \(1\)
  • 提前终止:如果在递归过程中发现不被整除的个数已经超过 \(1\),立即停止搜索。这种“剪枝”保证了每次询问只会完整走过最多两条通往叶子的路径,单次询问复杂度实为 \(O(\log n)\)

CF1209H (Moving Walkways)

题意\(n\) 个传送带,从 \(a_i\)\(b_i\),速度 \(s_i\)。其余为空地,人以 \(1+v\) 行走,\(v \in [-1, 1]\)(在传送带上有 \(v_i\) 加成)。
约束条件:\(E(x) = \int_0^x (1-v(t)) dt \ge 0\)。求 \(T_{min}\)

:本题的核心难点有两点:
第一、如何消除掉没用的变量 \(v\)
第二、如何贡献每段的能量。

假设在一段长度为 \(L\) 的路径上,速度为 \(s\) 的带上保持 \(v\) 速:

  • 实速:\(v + s\)
  • 时间 \(cost\)\(t = \frac{L}{v+s}\)
  • 能量变化 \(\Delta E\)\((1-v) \cdot t\)

我们需要保证 \(E\) 在任何时刻 \(\ge 0\)\(T = \sum t_i\) 最小。
\(\Delta E = t(1-v) = t(1 - (\frac{L}{t} - s)) = t(1+s) - L\),得:
\(t = \frac{1}{1+s} \Delta E + \frac{L}{1+s}\)

观察\(t\)\(\Delta E\) 成正比,比例系数 \(k = \frac{1}{1+s}\)

  • \(\Delta E \downarrow \Rightarrow\) 走得快,耗能 \(\Rightarrow t \downarrow\)
  • \(\Delta E \uparrow \Rightarrow\) 走得慢,积能 \(\Rightarrow t \uparrow\)

\(k = \frac{1}{1+s}\),我们考虑按时间轴分段贪心。这样能保证 \(\Delta E \ge 0\) 恒成立的同时,找系数最小的 \(\frac{1}{1+s}\) 让其积能备用效率最高。
先使 \(v=-1\) 奔跑,再处理亏欠问题即可。复杂度 \(O(n \log n)\)
(注:\(\Delta E = (1-v) \frac{L}{v+s} \in [-\frac{L}{2+s}, \frac{L}{s}]\),不能越界。此处 \(\frac{L}{s}\) 即为 lift energy。)

「 补充建议:关于贪心与线段树 」

  • 前缀和约束:能量约束 \(\int (1-v)dt \ge 0\) 本质上是要求 \(\sum \Delta E_i \ge 0\) 对所有前缀都成立。
  • 贪心策略补充:将所有物理分段(传送带和空地)按 \(s\) 从小到大排序。
  • 对于 \(s\) 较小的段,系数 \(k\) 较大,这意味着在这段“积攒”单位能量代价较小,或者说“消耗”单位能量换取的时间缩短最多。
  • 数据结构辅助:可以使用线段树维护当前所有位置的“剩余能量空间”。当我们试图在某一段(\(s\) 较小)尽量减少 \(\Delta E\) 以提速时,需要检查该位置之后的所有前缀能量是否仍 \(\ge 0\)。这转化为线段树上的区间减区间最小值查询

CF1423H Virus (撤销树, trick)

题意:给一个 \(n\) 点的图,\(q\) 次询问。每条边在第 \(k\) 天后被删除(\(x \to x + k\))。

  • 询问 1:\(x, y\) 连边。
  • 询问 2:\(z\) 询问 \(z\) 所在的块大小。
  • 询问 3:下一天。

:此题是经典的撤销树板题(线段树分治操作离线)。将每条边的作用区间置于 \(O(\log q)\) 个节点上标记永久化。利用按秩合并的可撤销并查集在树上作前序遍历即可。

「 补充建议:关于时空复杂度与细节 」

  • 区间定义:若一条边在第 \(D\) 天加入,其存活时间为 \(k\) 天,则该边在线段树上的覆盖区间为 \([D, \min(D+k-1, \text{TotalDays})]\)
  • 可撤销并查集 (Rollback DSU):由于线段树分治需要回溯,并查集不能使用路径压缩(因为它会破坏树的结构,导致无法简单回溯),而必须使用按秩合并 (Union by Rank/Size)
  • 回溯操作:进入子树时进行 union 并将修改存入栈中;离开子树时,根据栈中的记录执行撤销,恢复 parentsize/rank 数组。
  • 复杂度\(O(q \log q \log n)\)。线段树贡献一个 \(\log q\),并查集贡献一个 \(\log n\)

CF1515E Phoenix and Computers

题意\(n\) 台电脑。你可以手动开启一台,且开启的两台中间若仅剩一台,则那台会自动开启。求形成不同开启方案的个数(方案是有序序列,手动开启的序列不同即为不同方案)。\(n \le 400, \text{mod } M\)

:经典思路:以一个自动电脑分界,进行连续段 DP。

  • 引理:对于长度为 \(k\) 的连续手动开启段,开启方案数为 \(2^{k-1}\)。(易证:最后开启的一定是两端之一,以此类推,除第一台外每台都有 2 种选择)。
  • DP 定义 1 (\(O(n^3)\)):定义 \(dp_{i, j}\) 表示处理了前 \(i\) 台,其中手动开了 \(j\) 台的方案数。(\(j \neq 0\))
    • \(dp_{i, j} = \sum_{len=1}^{i} dp_{i-len-1, j-len} \times 2^{len-1} \times \binom{j}{len}\) (插空法,这里的 \(i-len-1\) 是为了空出一个自动开启的电脑作为分隔)。

解 2:连续段 DP(优化至 \(O(n^2)\)
这种典型优化将电脑动态加入,按顺序处理。

  • \(f_{i, j}\) 表示当前手动开启了 \(i\) 台,形成了 \(j\) 个连续段。
  • 转移策略
    • 新开段\(f_{i+1, j+1} \leftarrow f_{i, j} \cdot (j+1)\) (新开一个手动开启段,有 \(j+1\) 个空位可放)。
    • 并入段首/尾\(f_{i+1, j} \leftarrow f_{i, j} \cdot (2j)\) (在现有的 \(j\) 个段的左端或右端加入一台电脑)。
  • 最终结果\(Ans = \sum_{i=1}^n f_{i, 1}\)。注意此处的 \(i\) 代表总共手动开启的台数,最后必须合并成 1 个大段。

「 补充建议:关于 Connected Component DP 的直观理解 」

  • 为什么引理是 \(2^{k-1}\) 想象一个长度为 \(k\) 的段,如果要全部手动开启且不触发自动开启,意味着你选的每一台新电脑必须与已开启的电脑相邻。例如 \(k=3\):方案可以是 (1,2,3), (2,1,3), (2,3,1), (3,2,1)。本质是在构造过程中,每次新增的电脑只能放在当前已开启连续块的左边或右边。
  • \(O(n^2)\) 转移的本质:这种方法实际上是在模拟“手动开启”的过程。当我们增加一个段 \(j \to j+1\) 时,我们是在两个已有的块之间预留了位置(这些位置最终会被“自动开启”填满)。
  • 关于自动开启的隐含条件:在 \(O(n^2)\) 的连续段 DP 中,段与段之间默认至少间隔一台电脑。当两个段最终合并时,中间的那台电脑就会被触发自动开启。

习题集

N2. (CF1514C) Product 1 Modulo N

题意:给定 \(n\),从 \(\{1, \dots, n-1\}\) 中选出最多的数,使其乘积模 \(n = 1\)
:显然在 \(\mathbb{Z}_n^\times\) 中选。由于 \(\mathbb{Z}_n^\times\) 是一个群,故其所有元素的乘积 \(\prod_{x \in \mathbb{Z}_n^\times} x \in \mathbb{Z}_n^\times\)
若该乘积 \(\equiv 1 \pmod n\),则全选;若该乘积 \(P \neq 1\),则只需剔除 \(P\) 即可(由于 \(P \in \mathbb{Z}_n^\times\),其逆元存在且乘积除以 \(P\) 必为 \(1\))。集合大小为 \(|\mathbb{Z}_n^\times|\)\(|\mathbb{Z}_n^\times|-1\)

「 补充建议:高斯对威尔逊定理的推广 」

对于 \(\mathbb{Z}_n^\times\) 中所有元素的乘积 \(P \pmod n\)
\(P \equiv -1 \pmod n\),当 \(n = 4, p^k, 2p^k\)\(p\) 为奇素数);
\(P \equiv 1 \pmod n\),对于其他情况。
这直接决定了我们是否需要剔除最后一个元素。

N3. \(\mathbb{Z}_{100}^\times\) 有原根吗?\(\mathbb{Z}_{100}^\times\) 是循环群吗?

:由中国剩余定理(群同构):\(\mathbb{Z}_{100}^\times \cong \mathbb{Z}_4^\times \times \mathbb{Z}_{25}^\times \cong C_2 \times C_{20}\)
由于 \(\gcd(2, 20) = 2 \neq 1\),根据有限交换群的性质,该直积不是循环群。故 \(\mathbb{Z}_{100}^\times\) 不存在原根。

「 补充建议:原根存在的充要条件 」

\(n\) 有原根(即 \(\mathbb{Z}_n^\times\) 是循环群)当且仅当 \(n = 1, 2, 4, p^k, 2p^k\),其中 \(p\) 是奇素数。

H7. (CF1016G) Appropriate Team

题意:求数组 \(a_n\)\((i, j)\) 对数,满足 \(\exists x, \text{ s.t. } \gcd(a_i, x) = 1\)\(\text{lcm}(a_j, x) = v\)
(注:原题是 \(\gcd(x, a_i) = X, \text{lcm}(x, a_j) = Y\)。通过保留所有 \(X|a_i\) 的整数并替换为 \(a_i/X\)\(\text{lcm}(\frac{X}{X}, \frac{a_j}{X}) = \frac{Y}{X}\) 来转化为上述问题)。
:即 \(x \perp a_i\)\(\text{lcm}(x, a_j) = v\)。由于 \(v\) 是固定的,直接对 \(v\) 进行质因数分解 (Pollard's Rho)。之后对每个 \(a_j | v\) 作质因子查询,对 \(b\) 有不满的因子直接让入 \(x\),再查询所有 \(a_i\) 中不含 \(x\) 因子的个数。(用 bitset 优化)

「 补充建议:状态压缩与 SOS DP 」

  1. \(v\) 的不同质因子为 \(p_1, p_2, \dots, p_k\)\(k\) 很小,最大约 15)。
  2. 对于每个 \(a_j\),其掩码 \(mask_j\)\(k\) 位为 1 当且仅当 \(v\)\(p_k\) 的幂次等于 \(a_j\)\(p_k\) 的幂次。
  3. 对于每个 \(a_i\),其掩码 \(mask_i\)\(k\) 位为 1 当且仅当 \(a_i\) 不含 \(p_k\) 因子(即 \(a_i \perp p_k\))。
  4. 问题转化为:求 \((i, j)\) 使得 \(mask_i \cup mask_j = (1 \ll k) - 1\)
  5. 除了 bitset,也可以使用 SOS DP (Sum Over Subsets)\(O(k \cdot 2^k)\) 内快速统计。

E11. (CF1179D) Fedor Runs for President

题意:给定一棵树,求加一条边后简单路径的最大数量。(\(n \le 5 \times 10^5\))
:一棵树加一条边形成一个基环树。新增路径数不好计数,考虑到 \(u \to v\) 原本只有 1 条路径,现在可能有 \(1/2\) 条。
考虑“不经过新边”的路径:恰好在基环树的子树内。要使总路径数最大化,就要使这些内部路径数 \(\sum \binom{s_i}{2}\) 最小化。即:
使 \(\sum s_i^2\) 最小化。
\(f_u = \min \{ f_v + (sz_u - sz_v)^2, sz_u^2 \}\)

由于换根 DP 极其困难,考虑枚举路径:\(Ans_x = \min_{y \neq z \in son(x)} \{ f_y + f_z + (n - sz_y - sz_z)^2 \}\)
考虑对 \(z\) 进行优化:取 \(p, q\)\(Ans_{x \to p} \le Ans_{x \to q}\)\(\frac{(f_p + sz_p^2) - (f_g + sz_g^2)}{sz_p - sz_g} \le 2(n - sz_y)\)
或最小化 \(2sz_y sz_z + G(z) + [G(y) + n^2]\)
斜率优化 / Li Chao Tree (LCT)

「 补充建议:关于转移方程的展开 」

\(Ans_x\) 的式子展开:
\(Ans_x = f_y + f_z + (n - sz_y - sz_z)^2\)
\(Ans_x = f_y + f_z + n^2 + sz_y^2 + sz_z^2 - 2n \cdot sz_y - 2n \cdot sz_z + 2sz_y sz_z\)
\(Ans_x = (f_y + sz_y^2 - 2n \cdot sz_y) + (f_z + sz_z^2 - 2n \cdot sz_z) + 2sz_y sz_z + n^2\)
这是一个经典的 \(Y = kX + B\) 形式,其中:
\(X = sz_z\), \(k = 2sz_y\), \(B = (f_y + sz_y^2 - 2n \cdot sz_y) + n^2\)
在遍历子树节点 \(x\) 时,维护已经遍历过的子节点的 \((sz_z, f_z + sz_z^2 - 2n \cdot sz_z)\) 构成的凸包,即可用斜率优化在 \(O(n \log n)\)\(O(n)\) 内解决。


H23. Frequency Problem (H.V.)

题意:数组 \(a_n\),求最长子段,使众数不唯一。
:首先一个相对明显的引理:这些众数中必包含原序列众数 \(M\)

  • 由 E 的思路(众数出现次数 \(> B\):枚举每个数值 \(x\),然后将 \(a_i = x\) 设为 \(+1\),将 \(a_i = M\) 设为 \(-1\),其余设为 \(0\)。找最长段和为 \(0\) 即可。这可以通过前缀和和哈希表 \(O(n)\) 实现。
  • 在 H 题目中
    • 若一个数的出现次数 \(> B\),则枚举这些 \(\le \frac{n}{B}\) 个数值作上述处理。
    • 剩下出现次数 \(\le B\) 的,枚举频率 \(f \in [1, B]\),进行双指针处理。
  • 总体复杂度\(O(n\sqrt{n})\)

「 补充建议:引理的直观理解 」

假设某个最长子段的众数是 \(x\)\(y\),且都不等于全局众数 \(M\)。如果 \(M\) 在该段中出现的次数少于 \(x\)\(y\),我们可以尝试向左右扩展这个子段。由于 \(M\) 是全局众数,在更大的范围内 \(M\) 的出现次数最终会追上 \(x\)\(y\)
实现小 Trick:在枚举频率 \(f\) 的双指针中,维护当前区间内“出现次数恰好为 \(f\) 的数的个数”,当该个数 \(\ge 2\) 时,更新答案。


H24. (CF1187E) Tree Painting

题意:给定一棵 \(n\) 个点的树,初始全白。作 \(n\) 步操作,每次选一个黑点旁的白点染黑,获得白连通块分数。求染树最大分数。
:注意到连通块是单独扩张的,并不存在并行。因此只需考虑每个节点为根时扩展连通块的方案权。又因为子树的扩张顺序(相对)不影响答案,不妨让其独立偏序地扩张。

  • \(dp_u = \sum dp_{son} + sz_u\)
  • 换根即可。简单。

「 补充建议:换根公式推导 」

\(f(u)\) 为以 \(u\) 为第一步染黑点(即以 \(u\) 为根)时的总得分:
\(f(u) = \sum_{v \in V} sz_v^{(u)}\),其中 \(sz_v^{(u)}\) 是以 \(u\) 为根时 \(v\) 的子树大小。
当根从 \(u\) 移动到相邻的 \(v\) 时:
\(f(v) = f(u) - sz_v + (n - sz_v) = f(u) + n - 2sz_v\)
只需要一次 DFS 求出 \(f(root)\) 和所有的 \(sz\),再通过第二次 DFS 进行换根转移即可得到所有点的答案。


H26. (CF1178G) The Awesomest Vertex

题意:一个 \(n\) 节点的有根树,编号 \(1 \sim n\)\(rt = 1\)。每个点关联 \(a_i, b_i\)。用 \(R(v)\) 表示 \(v\) 的所有祖先(包括 \(v\))的集合,\(v\) 的“精彩度”为:

\[\left| \sum_{w \in R(v)} a_w \right| \cdot \left| \sum_{w \in R(v)} b_w \right| \]

操作

  1. 1 v x\(a_v \leftarrow a_v + x\)
  2. 2 v:查询以 \(v\) 为根的子树中精彩度最大值。

:考虑操作 1,其本质是对整棵子树的 \(a\) 操作,即 DFS 序上的一段区间修改。
操作 2 则是查询区间内最大的积。令 \(A_n = \sum_{w \in R(v)} a_w, B_n = |\sum_{w \in R(v)} b_w|\)(注意 \(B_n\) 是静态的),问题变成:区间加 \(A_n\) 和查询区间 \((|A_n| \cdot B_n)_{max}\)
即维护 \(f(x) = B_n \cdot |A_n + x|\) 的最大值。

  • 分块 + LCST:区间加用分块 Tag + 边缘暴力重建,查询用块内查询 Tag + 边缘暴力。
  • 复杂度\(O(n\sqrt{n} \log V)\),需要卡常。

「 补充建议:关于绝对值与凸包优化 」

  • 处理绝对值:由于存在绝对值,我们需要维护两个凸包:一个维护 \(B_n \cdot A_n\) 的最大值,另一个维护 \((-B_n) \cdot A_n\) 的最大值(即最小值的相反数)。
  • 关于 \(B_n\) 的符号:在计算 \(B_n = |\sum b_w|\) 时,直接取绝对值,这样 \(f(x)\) 就变成了关于 \(A_n\) 的线性函数 \(y = kx + b\) 的形式,其中斜率 \(k = B_n\)
  • 分块策略优化:由于 \(B_n\) 是静态的,每个块内的直线斜率是固定的。在预处理时,我们可以对每个块内的直线按斜率排序并建立静态凸包。当区间加 \(x\) 时,相当于在凸包上移动最优决策点。如果 \(x\) 始终为正,指针单调移动;若 \(x\) 可正可负,需在凸包上二分。这样可以将单次操作复杂度降至 \(O(\sqrt{n} \log \sqrt{n})\)\(O(\sqrt{n})\)

E30. (CF1290E) Cartesian Tree

题意:给定一个 \(1 \sim n\) 的排列 \(A_n\)。对于每个 \(k \le n\) 的前缀子序列建立笛卡尔树(大根堆),求所有子树大小之和 \(S_k\)
:由于是从小到大依次加入元素,新加入的点在数值上永远是当前最大的。
在笛卡尔树中,一个节点的子树大小等于其作为区间最大值所覆盖的范围长度,即 \(R_i - L_i + 1\),其中 \(L_i\)\(R_i\) 是左右两侧第一个比它大的元素的下标。
我们要维护每个点左右第一个比它大的 \(L_k\)\(R_k\)。支持以下操作:

  • ① 区间加/减 1
  • ② 单点修改(加入新值)
  • ③ 区间取 \(\min/\max\)
  • ④ 区间求和
    此即为 Segment Tree Beats。(需另外维护某一子段区间上的“有效值”个数,即当前已插入的元素个数)。

「 补充建议:关于 \(L_i\)\(R_i\) 的演变 」

  • 当我们插入数字 \(k+1\) 时,假设其位置为 \(pos\)
  • 对于所有位置 \(i < pos\) 的元素,\(R_i\) 会与 \(pos\)\(\min\)
  • 对于所有位置 \(i > pos\) 的元素,\(L_i\) 会与 \(pos\)\(\max\)
  • 同时,由于新插入了元素,所有原本在 \(pos\) 右侧的下标都会 \(+1\)
  • 这正是 Segment Tree Beats 处理区间 \(chmin/chmax\) 的经典舞台。最终答案为 \(\sum (R_i - L_i - 1)\)

E31. (CF1340F) Nastya and CBS

题意:动态维护一个括号序列:

  • 1 i t:修改 \(i\) 位置为字符 \(t\)(正左负右)。
  • 2 l r:查询 \([l, r]\) 是否是一个“合法的括号序列”。
    注:本题的括号种类是任意的。

:考虑类似线段树维护括号序列合法性的解法。对任意节点维护:lenL, lenR, hL, hR, bad
其中 lenL/lenR 表示左/右剩余未匹配的括号长度,hL/hR 表示对应的哈希值,bad 表示这一段是否已经由于括号类型不匹配而失败。

考虑 pushup:要从 \(u\) 的左子节点序列提取长度为 \(len\) 的哈希值:
在线段树中递归:先右后左。若 \(len \le rc.lenL\),返回 ghL(rc, len)。否则返回:ghL(lc, len - rc.lenL)rc.hL。(注:Hash 策略需支持合并)。

pushup 时判断

  • bad 属性:子节点若有 bad 则父节点为 bad
  • ② 匹配:比较 min(lc.lenL, rc.lenR) 长度的括号序列。
  • ③ 哈希:二者哈希值(对应长度)是否相等(通过 ghL/ghR 获取)。
  • ④ 更新:更新合并后的长度和哈希值。

「 补充建议:关于复杂度与哈希 」

  • 复杂度:单次 pushup 包含一个 \(O(\log n)\) 的递归查询,因此总复杂度为 \(O(q \log^2 n)\)。在 \(10^5\) 的数据范围内表现良好。
  • 哈希选择:建议使用双哈希以避免冲突。由于需要进行类似字符串的拼接,使用 \(H(S+T) = H(S) \cdot P^{|T|} + H(T)\) 的经典公式。
  • 递归查询函数 get_hash:这是本题的精髓。它允许我们在不显式存储所有前缀哈希的情况下,利用线段树的结构在对数时间内提取任意长度的括号匹配序列哈希。

E32. (ABC287Ex) Directed Graph and Query

题意\(n\)\(m\) 边的有向图,点从 \(1 \sim n\) 编号,边连 \(a_i, b_i\)
「成本」:路径上 \(\max\) 编号。
对于 \(x=1, 2, \dots, Q\),解决以下问题:从 \(s_x\)\(t_x\) 的最小「成本」,或 \(-1\)

:考虑“逐个加入”编号点 \(k: 1 \sim n\),并在每次加入时重新更新连通性。
本质就是 Floyd-Warshall 算法的变体。
利用 bitset 优化到 \(O(\frac{n^3}{w})\)

「 补充建议:离线处理与具体实现 」

  • 算法核心:我们关心的是只经过编号 \(\le k\) 的点时,\(s\)\(t\) 是否连通。
  • 步骤
    1. 将查询按“最小成本”期望(即可能的 \(k\))进行思考,但由于我们需要求的是最小的 \(k\),实际上我们可以运行 \(n\) 轮增量更新。
    2. \(dist[i]\) 为一个 bitset,表示从点 \(i\) 出发可以到达哪些点。
    3. 当加入点 \(k\) 时:
      如果点 \(i\) 能到达 \(k\)dist[i][k] 为真),则点 \(i\) 现在也能到达点 \(k\) 能到达的所有地方:dist[i] |= dist[k]
    4. 每次加入 \(k\) 后,检查所有尚未解决且满足 \(s, t\) 在当前 \(dist\) 矩阵中连通的询问,记录答案为 \(k\)
  • 性能建议:对于 \(N=2000\)\(N^3/w \approx 1.25 \times 10^8\),在 2 秒的时间限制内使用 bitset 优化是非常稳妥的。

posted @ 2026-01-30 18:22  Tnuzy_plzro  阅读(0)  评论(0)    收藏  举报