算法竞赛知识点手册

1. 基础贪心:邻项交换与证明

贪心算法的精髓在于“局部最优”导向“全局最优”。然而,其正确性并非总是显而易见的,需要严谨的数学证明。邻项交换(Exchange Argument)是证明一类排序相关贪心策略最经典、最强大的武器。其核心思想是:假设存在一个不同于贪心解的最优解,我们可以通过一系列“邻项交换”操作,在不降低解的质量的前提下,逐步将该最优解调整为贪心解。

这个过程通常遵循反证法。我们假设最优解 \(S_{opt}\) 在某个位置首次与贪心解 \(S_{greedy}\) 出现差异。设贪心策略是基于某种排序规则,我们将最优解中不符合该规则的相邻元素进行交换。关键在于证明这次交换不会使解变得更差。如果能证明这一点,我们就能说明任何最优解都可以被转化为贪心解,从而证实了贪心策略的正确性。例如,在经典的活动安排问题中,按结束时间排序的贪心策略,其正确性就可以通过邻项交换来完美证明。从更抽象的视角看,这构建了一个从任意最优解到特定贪心解的“路径”,确保了贪心解的“最优性代表”地位。

2. 最短路 (一):核心算法概览

最短路问题是图论的基石,不同算法适用于不同场景,其背后蕴含着动态规划与贪心思想的交辉。

  • Bellman-Ford & SPFA:两者都基于动态规划的松弛(Relaxation)操作,能处理带负权边的图。Bellman-Ford算法执行 \(V - 1\) 轮迭代,每轮对所有边进行松弛,其状态转移方程可视为 \(d(k, v) = \min_{u \to v} \{ d(k - 1, u) + w(u, v) \}\) ,表示最多经过 \(k\) 条边到达 \(v\) 的最短路。SPFA(Shortest Path Faster Algorithm)是Bellman-Ford的队列优化版本,它只将距离被更新的节点重新入队,在稀疏图和随机数据上表现出色,但其最坏复杂度与Bellman-Ford相同,且在特定构造的图上容易被卡到上限。两者的共同优势在于能够检测负权环。
    Dijkstra: 这是一种基于贪心策略的算法, 要求所有边权非负。它维护一个已确定最短路径的顶点集合 \(S\) , 每次从未在 \(S\) 中的顶点中, 选取一个距离源点最近的顶点 \(u\) 加入 \(S\) , 并用 \(u\) 来松弛其所有出边。正确性的根基在于, 当边权非负时, 一旦一个顶点的最短路被确定, 它不可能再被其他路径更新。使用优先队列 (如二叉堆) 优化后, 其复杂度为 \(O(E \log V)\)
  • Floyd-Warshall:用于计算任意两点间(All-Pairs)的最短路。它采用动态规划思想,其核心状态 \(d(k, i, j)\) 表示从 \(i\)\(j\) 只允许经过编号为1到 \(k\) 的顶点的最短路径长度。状态转移方程为: \(d(k, i, j) = \min(d(k - 1, i, j), d(k - 1, i, k) + d(k - 1, k, j))\)\(O(V^3)\) 的复杂度使其适用于稠密图。
  • Johnson: 在稀疏图上解决带负权边的全源最短路问题。它巧妙地结合了 Bellman-Ford 和 Dijkstra: 首先, 新建一个虚拟源点 \(s\) , 向所有顶点连一条权值为 0 的边, 然后用 Bellman-Ford 计算到所有顶点的最短路 \(h(v)\) 。若无负环, 则将每条边 \((u, v)\) 的权值 \(w(u, v)\) 更新为 \(w'(u, v) = w(u, v) + h(u) - h(v)\) 。可以证明新的边权 \(w'\) 非负, 且任意两点间的最短路径在新图中保持不变 (路径长度的增量为常数)。之后对每个点运行一次 Dijkstra 即可。
  • 差分约束系统:这是一种将特定形式的不等式组与最短路问题联系起来的优美模型。对于一组形如 \(x_{j} - x_{i} \leq c_{k}\) 的不等式,我们可以构造一张图,其中每个变量 \(x_{i}\) 对应一个节点。对于每个不等式 \(x_{j} - x_{i} \leq c_{k}\) ,我们从节点 \(i\) 向节点 \(j\) 连一条权值为 \(c_{k}\) 的边。求解这组不等式等价于在该图上求解最短路。若图中存在负权环,则不等式组无解。

3. 数据结构入门:分治思想的体现

数据结构是算法竞赛的“内功”,其核心在于高效地组织和处理数据。

  • 线段树 (Segment Tree): 线段树是处理区间问题的瑞士军刀。它将一个区间 \([1, N]\) 递归地二分, 直到成为单个元素, 从而构建一棵二叉树。树中每个节点代表一个子区间, 并存储该区间的聚合信息(如和、最值)。对于区间查询或更新, 一个大区间可以被拆分为 \(O(\log N)\) 个树上节点的代表区间。线段树的强大在于其可合并性 (associativity), 任何满足结合律的操作都可以用线段树来维护, 例如矩阵乘法、最大子段和等。
  • 主席树 (Persistent Segment Tree): 主席树, 或称可持久化线段树, 是一种能够查询数据结构历史版本的高级技巧。它并非完全复制整棵树, 而是在修改时只创建从根到被修改叶节点路径上的新节点, 而未受影响的子树则直接共享。这使得每次修改仅增加 \(O(\log N)\) 的空间。其经典应用是静态查询区间第 \(k\) 小, 通过对值域建立线段树, 并按原序列的顺序逐个插入元素, 形成 \(N\) 个版本, 版本 \(i\) 的树维护了前 \(i\) 个元素的权值信息。查询区间 \([l, r]\) 的第 \(k\) 小就变成了在版本 \(r\) 和版本 \(l - 1\) 的树上进行差分和查询。
  • 平衡树 (Balanced Binary Search Tree): 平衡树(如 Treap, Splay, AVL, Red-Black Tree)维持了二叉搜索树的性质,同时通过旋转等操作保证树的高度始终为 \(O(\log N)\) ,从而确保了插入、删除、查找等操作的效率。在竞赛中,Treap(树堆)因其代码简单、随机化性质稳定而广受欢迎,它为每个节点额外赋予一个随机优先级,同时满足 BST 性质和堆性质。Splay Tree(伸展树)则通过在每次访问后将节点“伸展”到根来达到均摊 \(O(\log N)\) 的复杂度,它在处理访问模式有局部性的数据时表现尤为出色。

4. 基础字符串算法:模式匹配的艺术

字符串算法是处理文本信息的核心工具,哈希、KMP 和 AC 自动机是其中最基础也最重要的三大算法。

  • 字符串哈希 (String Hashing): 哈希是一种将字符串映射为数字的随机化技术, 从而能以 \(O(1)\) 的时间复杂度比较两个子串是否相同。其核心思想是多项式哈希: 将字符串看作一个 \(P\) 进制的数, 其中 \(P\) 是一个大素数, 然后对另一个大模数 \(M\) 取模。即字符串 \(S = s_{1} s_{2} \dots s_{L}\) 的哈希值为:

\[H (S) = \left(\sum_ {i = 1} ^ {L} s _ {i} \cdot P ^ {L - i}\right) (\mathrm {m o d} M) \]

为了降低哈希碰撞的概率,通常采用双哈希(两个不同的模数 \(M_{1}, M_{2}\) )或多个哈希。通过预处理前缀哈希值,可以 \(O(1)\) 计算任意子串的哈希值,极大地简化了许多字符串匹配问题。

  • KMP (Knuth-Morris-Pratt): KMP 算法实现了在线性时间内完成单个模式串在主串中的匹配。其精髓在于利用匹配失败后的信息, 避免主串指针的回溯。它通过预处理模式串, 计算出一个 next 数组 (或称 fail 数组), next[i]表示模式串前缀 \(T[1..i]\) 的最长公共前后缀 (Proper Prefix Suffix) 的长度。当匹配在主串 \(S[i]\) 和模式串 \(T[j]\) 处失配时, 无需从 \(S\) 的下一位重新开始, 而是直接将模式串向右移动, 让其 next \([j - 1]\) 位置对准 \(S[i]\) , 继续比较。这本质上是利用了已知匹配部分的信息, 进行了最大程度的“有效”移动。
  • AC 自动机 (Aho-Corasick Automaton): AC 自动机是 KMP 算法在多模式串匹配问题上的推广。它将多个模式串构建成一个 Trie 树 (字典树), 然后为 Trie 上的每个节点计算一个失配指针 (fail pointer), 这一点与 KMP 的 next 数组思想一脉相承。一个节点 \(u\) 的失配指针指向的节点 \(v\) , 代表的字符串是 \(u\) 所代表字符串的最长后缀, 且该后缀同时是某个模式串的前缀。在主串上匹配时, 我们沿着 Trie 边转移; 若无法转移, 则跳转到其失配指针继续匹配。这样, 只需遍历主串一次, 就能找出所有模式串在主串中的出现位置。

5. 同余数论 (一):模意义下的代数运算

数论是组合计数和密码学等领域的基础。模算术是其核心,解决的是在整数环 \(\mathbb{Z}_n\) 上的运算问题。

·费马小定理(Fermat'sLittleTheorem):若p是一个素数,且整数α不是p的倍数,则有:

\[a ^ {p - 1} \equiv 1 (\mathrm {m o d} p) \]

这是欧拉定理的一个特例,在模素数的幂运算中极为重要。它可以用来快速计算模逆元,当 \(p\) 为素数时, \(a\) 的模 \(p\) 逆元就是 \(a^{p-2} \pmod{p}\) 。同时,它也是许多素性测试(如 Miller-Rabin)的理论基础。

  • 乘法逆元 (Multiplicative Inverse): 在模 \(m\) 意义下, 整数 \(a\) 的乘法逆元 \(a^{-1}\) 是指满足

\(a\cdot a^{-1}\equiv 1(\mathrm{mod}m)\) 的整数。逆元存在的充要条件是 \(\gcd (a,m) = 1\) 。当 \(m\) 为素数时,可用费马小

定理求解。当 \(m\) 不一定是素数时,需要使用扩展欧几里得算法(ExtendedEuclideanAlgorithm,

exgcd)。该算法用于求解方程 \(ax + by = \gcd(a, b)\) 的一组整数解 \((x, y)\) 。求解 \(a \cdot x \equiv 1 \pmod{m}\) 就等价于求解 \(ax + my = 1\) ,前提是 \(\gcd(a, m) = 1\)

  • 线性同余方程组:求解形如 \(x \equiv a_{i} (\mathrm{mod} m_{i})\) 的方程组。当模数 \(m_{i}\) 两两互素时,可以使用中国剩余定

理 (Chinese Remainder Theorem, CRT)构造出通解。当模数不一定两两互素时,则需要扩展中国剩余

定理 (exCRT)。exCRT 的思想是逐个合并方程。假设已经求出前 \(k - 1\) 个方程的通解 \(x = A + t \cdot M_{t}\)

其中 \(M = \operatorname{lcm}(m_1, \ldots, m_{k-1})\) 。将其代入第 \(k\) 个方程,得到 \(A + t \cdot M \equiv a_k \pmod{m_k}\) ,这是一

个关于 \(t\) 的线性同余方程 \(t\cdot M\equiv a_k - A\) (mod \(m_{k}\) ),可以用exgcd求解 \(t\)

  • Lucas 定理:用于在模素数 \(p\) 的意义下快速计算大组合数 \(\binom{n}{m}\) 。Lucas 定理指出:

\[\binom {n} {m _ {i}} \equiv \prod_ {i = 0} ^ {k} \binom {n _ {i}} {m _ {i}} (\mathrm {m o d} p) \]

其中 \(n = n_{k}p^{k} + \dots +n_{1}p + n_{0}\)\(m = m_k p^k +\dots +m_1p + m_0\)\(n\)\(m\)\(p\) 进制表示。这个定

理将一个大的组合数计算问题,分解为一系列在 \(p\) 进制下每一位的、小的组合数计算问题。这些小的组合

\(\binom{n_i}{m_i}\) 可以通过预处理阶乘和逆元在 \(O(p)\) 时间内解决。

6. 基础构造交互模型:思维的起手式

构造题与交互题是算法竞赛中衡量创造性思维和问题分解能力的重要题型,没有固定的算法套路,但存在通用的思考方向。

  • 从特殊到一般:面对一个构造问题,首先尝试解决最小、最简单的情况,如 \(N = 1,2,3\) 。观察这些小样例的解,寻找规律、对称性或周期性。能否将 \(N\) 的解通过 \(N - 1\)\(N / 2\) 的解构造出来?这种归纳和递归的思路是构造的核心。
  • 寻找不变量与单调性:在交互过程中,每次操作会改变系统的状态。思考哪些量在操作中保持不变(不变量),或者哪些量是单向变化的(单调性,如问题的规模总在减小)。利用不变量可以排除大量无效状态,而利用单调性则能保证算法的收敛和终止。
  • 信息论视角:在交互题中,你的目标是用最少的查询次数确定答案。思考每个查询最多能提供多少信息。例如,一个二元(是/否)查询最多提供1bit的信息。如果答案有 \(K\) 种可能性,你至少需要 \(\lceil \log_2K\rceil\) 次查询。这种“信息熵”的思考方式可以帮助你判断一个策略是否可能达到最优,并指导你设计能最大化信息获取的查询。
  • 对手思维(Adversary Argument):想象一个“对手”在你每次查询后,都会给出使你最难受的、但与之前所有回答都兼容的答案。你的策略必须在最坏情况下依然有效。这种思维方式能帮你构建出鲁棒性强的算法,覆盖所有可能的情况。

7. 最小生成树:网络连接的经济学

最小生成树(Minimum Spanning Tree, MST)旨在寻找一个无向连通图的权值最小的生成树。这背后是贪心思想的又一经典体现,其正确性由“切割性质”这一优美的理论所保证。

  • Kruskal & Prim:这两个是构建MST的主流算法。Kruskal算法将所有边按权值从小到大排序,然后依次遍历,如果一条边连接的两个顶点尚不连通,则将其加入生成树中。其正确性在于,每次加入的都是当前“最安全”的边,它绝不会使我们错过最优解。Prim算法则像Dijkstra一样,从一个起始点开始,“生长”出一棵树。它维护一个已在树中的顶点集合 \(S\) ,每次选择一条连接 \(S\) 内外且权值最小的边加入树中。Kruskal适合稀疏图,依赖于高效的排序和并查集;Prim适合稠密图,依赖于优先队列。
  • Borůvka:这是一个相对冷门但思想独特的算法。它以一种并行的方式工作:在每一轮中,图中的每一个连通分量都找出一条连接自己到其他分量的权值最小的边,然后将这些边全部加入。这个过程会合并连通分量,并不断重复,直到整个图连通。由于每轮连通分量的数量至少减半,算法至多有 \(O(\log V)\) 轮,这使得它在并行计算和处理某些特殊类型的图时非常高效。
  • 次小生成树 (Second MST): 求解权值第二小的生成树。一个标准方法是先求出一个最小生成树 \(T_{mst}\) , 其权值为 \(W_{mst}\) 。次小生成树 \(T_{smst}\) 必然是由 \(T_{mst}\) 替换掉一条边, 并加入一条不在 \(T_{mst}\) 中的边得到的。我们遍历所有不在 \(T_{mst}\) 中的边 \((u, v)\) , 将其加入 \(T_{mst}\) 会形成一个唯一的环。为了保持树的结构, 我们必须从这个环中移除一条边。为了使新树的权值增量尽可能小, 我们应该移除环上除了 \((u, v)\) 之外权值最大的那条边。遍历所有非树边, 计算出所有可能的候选生成树权值, 取其中的最小值即为次小生成树的权值。使用树上倍增或树链剖分可以高效查询路径上的最大边权。
  • 最小生成树计数:计算一个图中不同但权值和相同的最小生成树有多少种。关键在于处理权值相同的边。我们将所有边按权值分组。然后从小到大依次处理每个权值 \(w\) 。假设当前图中有 \(c\) 个连通分量,处理权值为 \(w\) 的这组边时,我们需要确定必须加入多少条权值为 \(w\) 的边才能最大程度地连通图。这可以通过一个“虚拟”的 Kruskal 过程实现:用并查集检查这些边能连接多少不想干的连通块。若需要选择 \(k\) 条边,而权值为 \(w\) 的边共有 \(m\) 条,则方案数为 \(\binom{m}{k}\) 。根据乘法原理,将所有权值组的方案数相乘即为总数。

8. 最短路 (二):模型的延展与深化

在掌握了基础最短路算法后,我们可以探索其在更复杂模型中的应用。

  • 最短路树 (Shortest Path Tree): 从源点 \(s\) 出发, 在一个图中可以构建一棵最短路树, 使得树上从 \(s\) 到任意顶点 \(v\) 的路径, 就是原图中 \(s\)\(v\) 的一条最短路。构建过程很简单: 运行 Dijkstra 或 SPFA 算法后, 对于每个顶点 \(v \neq s\) , 必然存在一个前驱节点 \(u\) 满足 \(d(v) = d(u) + w(u, v)\) 。保留所有这样的边 \((u, v)\) , 会形成一个有向无环图 (DAG)。在这个 DAG 中以 \(s\) 为根的任意一棵生成树都是一棵合法的最短路树。最短路树在需要分析所有最短路性质的问题中非常有用。
  • 平面图最小割: 这是一个惊人的对偶理论的应用。在无向平面图中, 求解 \(s - t\) 最小割的复杂度通常较高。但利用平面图的对偶性, 问题可以转化为最短路。构造其对偶图 \(G^{*}\) : 原图的每个面成为 \(G^{*}\) 的一个顶点, 原图每条边 \(e\) 对应 \(G^{*}\) 中一条连接其两侧面对应顶点的边, 权值与 \(e\) 相同。找到穿过 \(s\)\(t\) 的那个面 (通常需要添加一条 \(t \rightarrow s\) 的无限大容量边, 从而将 \(s\)\(t\) 分在两个特定面上), 这两个面在对偶图 \(G^{*}\) 中对应的顶点 \(s^{*}, t^{*}\) 之间的最短路长度, 就等于原图 \(G\) 中的 \(s - t\) 最小割。
  • 同余最短路:这是一类解决特定数论或组合问题的模型,形式通常是:给定一些整数 \(a_1, \ldots, a_n\) ,问它们能凑出的、或不能凑出的满足某种条件的数。我们可以把问题转化为在模 \(m\) 的剩余系上的最短路。通常取某个较小的 \(a_i\) 作为模数 \(m\) ,建立 \(m\) 个点 \(0, 1, \ldots, m - 1\) 。点 \(u\) 的最短路 \(d(u)\) 表示能凑出的、模 \(m\)\(u\) 的最小数。对于每个 \(a_j\) ,我们从每个点 \(u\) 向点 \((u + a_j) \pmod{m}\) 连一条权值为 \(a_j\) 的边。从源点0运行 Dijkstra,就能求出所有 \(d(u)\)
  • \(k\) 短路:求解从 \(s\)\(t\) 的第 \(k\) 短的路径长度(允许重复经过点和边)。高效的解决方法是 \(\mathsf{A}^{\star}\) 算法。 \(\mathsf{A}^{\star}\) 是一种启发式搜索,其节点的估价函数为 \(f(v) = g(v) + h(v)\) ,其中 \(g(v)\) 是从 \(s\) 到当前节点 \(v\) 的实际代价, \(h(v)\) 是从 \(v\)\(t\) 的预估代价。为了保证找到的是最优解,启发函数 \(h(v)\) 必须是“可接受的”(即不高于实际最短距离)。在 \(k\) 短路问题中,我们令 \(h(v)\) 就等于 \(v\)\(t\) 的真实最短路长度,这可以通过在反图上从 \(t\) 跑一次 Dijkstra 预处理出来。然后,我们用一个优先队列维护 \(f(v)\) ,每次取出最小的节点扩展,当第 \(k\) 次取出终点 \(t\) 时,其对应的 \(g(t)\) 值就是答案。

9. 扫描线与分治:降维打击的艺术

扫描线与分治是处理几何问题和高维数据问题的强大思想工具,其核心在于将复杂问题分解为更易于处理的低维或小规模子问题。

  • 扫描线 (Sweep-line): 扫描线算法将一个静态的 \(D\) 维问题转化为一个 \(D - 1\) 维的动态问题。以经典的矩形面积并为例, 这是一个二维静态问题。我们可以想象一条垂直的扫描线从左到右扫过整个平面。只有在遇到矩形的左边界或右边界时, 图形的状态才会发生改变。我们将这些横坐标作为“事件点”, 排序后依次处理。在两个事件点之间, 扫描线上的被覆盖情况是不变的。我们用一个数据结构 (如线段树) 来维护当前扫描线在 \(y\) 轴方向上的覆盖信息。遇到左边界, 就在线段树上进行区间增加操作; 遇到右边界, 就进行区间减少。每次处理完一个事件点, 计算当前事件点到下一个事件点之间的矩形条的面积, 累加即为总面积。
  • CDQ 分治:由陈丹琦引入的、处理多维偏序问题的离线算法。它是一种特殊的分治思想,常用于解决带修改的查询问题。我们将操作序列(修改和查询)按时间(或其他维度)一分为二。首先递归处理左半部分。然后,计算左半部分的修改对右半部分的查询产生的影响。最后,递归处理右半部分。关键在于,计算跨区间贡献时,所有修改都发生在所有查询之前,问题被静态化,从而降低了处理难度。例如,一个三维偏序问题(时间、x、y),通过对时间分治,跨区间的子问题就变成了一个二维偏序问题,可以用树状数组或扫描线等方法高效解决。
  • 整体二分 (Overall Binary Search): 适用于一类特殊问题: 答案具有单调性, 且可以批量处理。通常题目包含多次查询, 每个查询都可以在一个值域上二分答案。整体二分不是对每个查询单独二分, 而是将所有查询放在一起, 对答案的值域 \([L, R]\) 进行二分。取中点 \(m i d\) , 然后将所有操作和查询分为两类: 那些与 \(\leq m i d\) 的答案相关的, 和那些与 \(>m i d\) 的答案相关的。这个划分过程通常需要一个数据结构辅助计算。然后, 将这两组查询 (以及相关的操作) 分别递归到 \([L, m i d]\)\([m i d + 1, R]\) 中去解决。这种方法将多次二分合并为一次分治, 通过批量处理降低了总复杂度。

10. 数论函数与筛法 (一):整数的内在规律

数论函数研究整数的性质,而筛法则为高效计算这些函数值提供了基础工具。

  • 素数筛法:快速找出一定范围内的所有素数。埃氏筛 (Sieve of Eratosthenes) 的思想是,从 2 开始,将每个素数的倍数都标记为合数,复杂度为 \(O(N \log \log N)\) 。线性筛 (Euler's Sieve) 则做到了每个合数只被其最小的质因子筛掉一次,从而达到 \(O(N)\) 的复杂度。线性筛在筛素数的同时,能顺便求出每个数的最小质因子,这为计算其他积性数论函数打下了坚实基础。

·狄利克雷卷积(DirichletConvolution):这是数论函数之间的一种二元运算,定义为: \((f*g)(n) = \sum_{d|n}f(d)g(\frac{n}{d})\)

其中 \(d \mid n\) 表示 \(d\)\(n\) 的所有正因子。狄利克雷卷积满足交换律、结合律,并且有单位元函数 \(\epsilon(n)\) (仅当 \(n = 1\) 时为 1,否则为 0)。许多数论函数关系都可以通过狄利克雷卷积优美地表达,例如 \(\mu * I = \epsilon\) (莫比乌斯反演), \(\phi * I = \mathrm{id}\) (欧拉函数性质),其中 \(I(n) = 1\) 为常数函数, \(\mathrm{id}(n) = n\) 为恒等函数。理解卷积是掌握莫比乌斯反演等进阶技巧的前提。

  • 数论分块 (Dirichlet K-th Root Trick): 用于快速计算形如 \(\sum_{i=1}^{N} \left\lfloor \frac{N}{i} \right\rfloor f(i)\) 的和式。其核心观察是, \(\left\lfloor \frac{N}{i} \right\rfloor\) 的值在一个连续的区间内是相同的。具体来说, 对于一个 \(i\) , 使得 \(\left\lfloor \frac{N}{i} \right\rfloor = k\) 成立的最大的最大的 \(j\)\(\left\lfloor \frac{N}{k} \right\rfloor\) 。因此, 我们可以将求和的 \(N\) 项, 根据 \(\left\lfloor \frac{N}{i} \right\rfloor\) 的取值划分成 \(O(\sqrt{N})\) 个块。每一块内的 \(\left\lfloor \frac{N}{i} \right\rfloor\) 值相同, 我们只需要快速计算出 \(f(i)\) 的区间和即可。这使得原本 \(O(N)\) 的求和过程被优化到 \(O(\sqrt{N})\) , 前提是 \(f(i)\) 的前缀和可以 \(O(1)\) 或快速求出。

11. 计数 DP:模型、转化与恒等式

计数类动态规划(Counting DP)是组合计数问题中最常用的方法,它要求逻辑严谨,状态定义清晰,转移方程无遗漏、无重复。

  • 模型刻画:计数 DP 的第一步是定义状态。状态 \(dp(i, j, \ldots)\) 必须能够“不重复、不遗漏”地覆盖所有合法方案。一个好的状态定义应该满足“无后效性”,即当前状态的决策只依赖于之前的状态,而与如何达到之前的状态无关。常见的 DP 模型有:线性 DP(如斐波那契数列)、背包 DP(如完全背包求方案数)、区间 DP(如括号序列计数)、树形 DP(如树上独立集计数)、状压 DP(如旅行商问题求路径数)。关键在于将一个大的计数问题,分解为若干个规模更小、结构相同的子问题。

  • 组合意义转化:有时候,DP的转移方程非常复杂,直接计算会导致超时。这时,可以尝试挖掘转移方程背后的组合意义。例如,一个形如 \(dp(i) = \sum_{j=0}^{i-1} dp(j) \cdot dp(i - 1 - j)\) 的转移,其组合意义可能对应着将一个大小为 \(i\) 的问题划分为两个子问题的过程,这正是卡特兰数的递推式。识别出这种组合模型,可以直接使用已知的组合数公式求解。反之,一个复杂的组合问题,也可以通过设计DP状态来理清其递推结构,最终可能发现DP方程本身就是一个组合恒等式的体现。

  • 组合恒等式优化:在 DP 转移中,常常出现含组合数的求和式。例如 \(dp(i) = \sum_{j=k}^{i} \binom{j}{k} \cdot f(j)\) 。直接计算这样的转移是低效的。此时,需要运用组合恒等式进行化简。例如,利用 \(\binom{n}{k} = \binom{n-1}{k} + \binom{n-1}{k-1}\) 进行递推,或者利用“范德蒙德卷积”、“吸收恒等式” \(k \binom{n}{k} = n \binom{n-1}{k-1}\) 等来变换求和的形式,使其能够被预处理前缀和或者进行其他形式的优化,从而加速 DP 过程。掌握常见的组合恒等式是优化计数类 DP 的重要内功。

12. 幂次的处理:代数工具箱

在算法竞赛中,对幂次、多项式的高效处理是解决许多计数、代数问题的关键。

  • 等比数列求和:形如 \(\sum_{i=0}^{n-1} a q^i\) 的和式在许多问题中都会出现,例如在计算哈希值或者某些分治算法的复杂度分析中。其求和公式为 \(S_n = a \frac{q^n - 1}{q - 1}\) (当 \(q \neq 1\) 时)。在模意义下,这需要计算 \(q - 1\) 的乘法逆元。当 \(q - 1\) 与模数不互质时(例如 \(q = 1\)\(q - 1\) 是模数的倍数),公式不再适用,需要具体分析。例如,当 \(q = 1 \pmod{p}\) 时,和为 \(a \cdot n \pmod{p}\)
  • 二项式展开:二项式定理 \((x + y)^n = \sum_{k=0}^{n} \binom{n}{k} x^k y^{n-k}\) 是组合数学的基石。它将一个幂次运算展开成一个和式,是许多计数问题推导的起点。例如,在容斥原理的推导中,它可以用来证明 inclusion-exclusion 公式。在生成函数中, \((1 + x)^n\) 正是“从 \(n\) 个不同物品中选 \(k\) 个”这一组合问题的生成函数。灵活运用二项式定理及其变体(如牛顿二项式定理 \((1 + x)^{\alpha}\) )是高级计数技巧的基础。
  • 斯特林数与下降幂:第一类斯特林数 \(\left[\begin{array}{c}n \\ k\end{array}\right]\) 表示将 \(n\) 个不同元素排成 \(k\) 个非空圆排列的方案数。第二类斯特林数 \(\left\{\begin{array}{l}n \\ k\end{array}\right\}\) 表示将 \(n\) 个不同元素划分到 \(k\) 个非空集合的方案数。它们与幂次之间有深刻的联系,是普通幂、上升幂和下降幂之间的“桥梁”。下降幂 \(x^{\underline{k}} = x(x - 1)\ldots (x - k + 1)\) 是一个 \(k\) 次多项式。普通幂 \(x^n\) 可以表示为下降幂的线性组合:

\[x ^ {n} = \sum_ {k = 0} ^ {n} \left\{ \begin{array}{l} n \\ k \end{array} \right\} x ^ {\underline {{k}}} \]

这个关系在处理某些涉及幂次求和的组合问题时非常有用,因为下降幂的组合性质往往比普通幂更好处理。

13. 树上数据结构问题:静态与动态的交织

树是一种基本而重要的图结构,围绕树的查询和修改催生了一系列高效的数据结构。

  • 树链剖分 (Heavy-Light Decomposition): 将一棵树剖分为若干条不相交的路径(重链),使得从根到任意节点的路径上最多只有 \(O(\log N)\) 条轻边和 \(O(\log N)\) 条重链。这样,对任意一条树上路径的操作,就可以被分解为对 \(O(\log N)\) 个连续区间(在重链的 DFS 序上是连续的)的操作。配合线段树等区间数据结构,即可实现对树上路径的查询与修改,时间复杂度通常为 \(O(\log^2 N)\)\(O(\log N)\)
  • 线段树合并 (Segment Tree Merging):当我们需要为树上的每个节点维护一个权值线段树(例如,统计子树内每种颜色的数量),并需要从子节点向父节点汇总信息时,线段树合并就派上了用场。合并两棵线段树的过程是递归的:如果一个节点在一棵树中存在而另一棵树中不存在,则直接继承;如果都存在,则递归地合并它们的左右子节点,并将当前节点的值相加。由于合并操作只在两棵树的交集部分消耗时间,合并的总复杂度可以被证明是与线段树总节点数相关的,通常在启发式合并的框架下能达到很好的均摊复杂度。
  • 启发式合并 (DSU on Tree): 这是处理一类树上子树统计问题的通用技巧。当需要计算每个节点子树的某些信息,且信息不方便从子节点直接递推时,可以采用启发式合并。我们对树进行深度优先搜索,对每个节点,先递归处理其所有轻儿子的子树(并清空其贡献),然后递归处理其重儿子的子树(并保留其贡献),最后,暴力地再次遍历所有轻儿子的子树,将它们的贡献加入到当前节点的答案中。因为一个节点每次作为轻儿子的子树被暴力统计时,其所在子树的大小至少会翻倍,所以每个节点最多被这样暴力统计 \(O(\log N)\) 次,总复杂度为 \(O(N \log N)\)
  • 点分治 (Centroid Decomposition): 一种处理树上路径问题的分治算法。它每次选取树的重心 (一个节点, 删除后最大子树的大小最小), 将所有经过重心的路径作为一类问题处理, 然后递归地对删除重心后形成的各个子树进行分治。由于每次选择重心, 问题的规模 (子树大小) 至少减半, 分治深度不超过 \(O(\log N)\) 。处理经过重心的路径时, 通常先计算出所有点到重心的路径信息, 然后用类似双指针或计数的方法合并来自不同子树的路径。点分治适用于静态的、与路径长度/权值相关的统计问题。

14.经典贪心模型:直觉背后的证明

除了邻项交换,贪心策略的正确性证明还有多种形式,理解这些经典模型有助于培养对贪心问题的直觉。

  • 拟阵 (Matroid): 拟阵是描述一类“独立性”系统最泛化的数学结构, 它为一大类贪心算法的正确性提供了坚实的理论基础。一个拟阵 \(M = (S, \mathcal{I})\) 由一个有限集 \(S\) (基集) 和一族 \(S\) 的子集 \(\mathcal{I}\) (独立集) 构成, 满足三个公理: 空集是独立的; 独立集的子集是独立的; 若 \(A, B \in \mathcal{I}\)\(|A| < |B|\) , 则存在 \(x \in B \setminus A\) 使得 \(A \cup \{x\} \in \mathcal{I}\) (增广公理)。对于带权重的拟阵, Rado-Edmonds 贪心算法——即按权值从大到小(或从小到大)遍历元素, 只要加入后仍保持独立性就加入——总能找到最大(或最小)权重的独立基。最小生成树的 Kruskal 算法和在一个线性无关向量集中找最大权重基, 都是拟阵贪心的经典例子。
  • 决策包容性 (Choice Property): 这是证明贪心正确性的另一种思路。它指的是, 对于一个问题, 存在一个最优解, 它包含了我们贪心做出的第一个选择。如果能证明这一点, 我们就可以放心地做出这个选择,然后问题就转化为一个规模更小的子问题。例如, 在活动安排问题中, 选择结束时间最早的活动。可以证明, 总存在一个最优解包含了这个活动。假设最优解 OPT 没有包含它, 而是包含了另一个在同一时间段的活动 \(a'\) , 我们可以用结束时间最早的活动 \(a\) 替换 \(a'\) , 得到的解不会更差, 且仍然合法。这个替换过程就体现了决策包容性。
    ·区间相关问题:一类常见的贪心模型是处理区间。例如,区间选点问题(选最少的点覆盖所有区间),贪心策略是按右端点排序,每次选择当前未被覆盖的区间的右端点。区间覆盖问题(用给定的区间集合覆盖一个大区间),贪心策略是从左到右,每次选择能覆盖当前最左未覆盖点,且向右延伸最远的区间。这些策略的共性是建立了一个明确的、单向的“进度”标准(如最右覆盖点),每次贪心选择都旨在最大化这个进度,从而保证了全局最优。

15. 强化数据结构:特定场景的利器

在基础数据结构之上,一些特化或高级的变体能够解决更复杂、更棘手的问题。

  • 单侧递归线段树 (Segment Tree Beats): 传统线段树处理的区间修改是统一的, 如区间加、区间赋值。而“吉司机线段树”(Segment Tree Beats)能够处理更复杂的区间修改, 如区间取 \(\min(x, c)\) 。其核心思想是, 在更新时, 如果当前节点的修改可以被“简单”地处理(例如, 区间所有值都小于等于 \(c\) ), 就直接打上标记返回; 如果不能(区间内有的值大于 \(c\) , 有的小于等于 \(c\) ),则暴力递归到子节点。通过在节点上维护最大值、次大值和最大值个数等额外信息, 可以证明在特定操作下, 每次暴力递归都会消除一种取值, 使得总的势能分析下的均摊复杂度依然是 \(O(\log N)\)
    ·K-DTree&李超树:两者都用于处理几何问题。K-DTree是一种高维空间划分数据结构,它交替地按不同坐标轴的中位数将点集划分,构建一棵二叉树。它擅长处理高维最近邻查询、区域查询等。虽然在维度较高时性能会退化,但在二维或三维的竞赛问题中非常实用。李超树(Li Chao Tree)是一种维护平面上若干条直线(或线段),并能快速查询在某个横坐标 \(x\) 处所有直线的最高(或最低) \(y\) 值的线段树。其每个节点维护一条在该节点对应区间中点处最优的“优势线段”。插入一条新线段时,通过与当前节点的优势线段比较,决定是替换、放弃还是递归下传到子区间。查询复杂度为 \(O(\log C)\) ,其中 \(C\) 是坐标范围。
  • 数据结构维护连续段:这是一类技巧,而非单一的数据结构,常用于处理排列或序列中“连续段”的动态变化。连续段指一个值域连续的子集,其在原序列中的位置也构成一个连续区间。这类问题通常可以用线段树或平衡树来维护。例如,用线段树维护一个区间的哈希值,通过哈希值的计算来判断一个区间内的元素值是否构成一个公差为1的等差数列。或者用链表/并查集维护连续段的边界,再用其他数据结构管理这些连续段的属性。这类技巧的关键在于找到一个高效的方法来识别和合并连续段。

16. 容斥与反演:计数的正反之道

容斥原理是组合计数中最基本也最强大的思想之一,而反演则是其在代数层面上的抽象和推广。

·容斥原理(Inclusion-Exclusion Principle):用于计算若干个集合的并集大小。其核心公式为:

\(\left|\bigcup_{i = 1}^{n}A_{i}\right| = \sum_{i}\left|A_{i}\right| - \sum_{i < j}\left|A_{i}\cap A_{j}\right| + \sum_{i < j < k}\left|A_{i}\cap A_{j}\cap A_{k}\right| - \dots +(-1)^{n - 1}\left|A_{1}\cap \dots \cap A_{n - 1}\right|\) 在计数问题中,它常被用于"恰好"与"至少/至多"之间的转换。如果直接计算"恰好满足 \(k\) 个条件"的方案数很困难,我们往往可以先计算"至少满足 \(k\) 个条件"的方案数(通常更容易),然后通过容斥原理得到最终答案。

  • 二项式反演 (Binomial Inversion): 是容斥原理的一种具体代数形式。它有两种常见形式, 若有两个序列 \(f\left( n\right)\)\(g\left( n\right)\) :

如果 \(g(n) = \sum_{i=0}^{n} \binom{n}{i} f(i)\) , 那么 \(f(n) = \sum_{i=0}^{n} (-1)^{n-i} \binom{n}{i} g(i)\)

如果 \(g(n) = \sum_{i=n}^{m} \binom{i}{n} f(i)\) , 那么 \(f(n) = \sum_{i=n}^{m} (-1)^{i-n} \binom{n}{i} g(i)\)

这里的 \(g(n)\) 通常表示“钦定/至少有 \(n\) 个”的方案数,而 \(f(n)\) 表示“恰好有 \(n\) 个”的方案数。二项式反演为从

“至少”到“恰好”的转换提供了直接的公式。

  • min-max 容斥:这是一个在期望和概率问题中非常有用的恒等式。对于一个集合 \(S\) ,其最大元和最小元可以通过子集的最小/最大元表示:

\(\max (S) = \sum_{0\neq T\subset S}(-1)^{|T| - 1}\min (T)\)
\(\min (S) = \sum_{\emptyset \neq T\subset S}(-1)^{|T| - 1}\max (T)\)

这个公式的强大之处在于,它将对一个集合求最大/最小值的期望,转化为了对所有子集求最小/最大值的期望的线性组合。在很多问题中,求某个事件“首次”发生的期望(对应min)是困难的,但求若干事件“都”发生的期望(对应max)可能相对容易,反之亦然。min-max容斥为这种转换提供了桥梁。

17. 进阶字符串算法:深度与广度

在基础字符串处理之上,更精妙的算法能揭示字符串内部更深层次的周期性和对称性。

  • Z-函数 (Z-algorithm) & Manacher: 两者都是在线性时间内处理字符串特定匹配问题的利器。Z-函数(或称扩展KMP)计算一个Z-数组, \(Z[i]\) 表示字符串S和其后缀 \(S[i..]\) 的最长公共前缀(LCP)的长度。它通过维护一个当前已知的匹配最远的区间 \([L, R]\) 来实现线性时间的计算,对于新的i,如果它在 \([L, R]\) 内,就可以利用已有的Z值来加速计算。Manacher算法专门用于解决最长回文子串问题。它通过在字符间插入特殊符号,将奇偶长度的回文串统一处理,并借鉴了Z-算法中利用已知信息加速计算的思想,维护一个回文半径最右的中心,实现了 \(O(N)\) 的复杂度。
  • 后缀数组 (Suffix Array, SA): 后缀数组是处理字符串问题的终极武器之一。它包含两个核心数组: sa 数组和 rank 数组。sa[i] 表示将所有后缀按字典序排序后, 排在第 \(i\) 位的后缀的起始位置。rank[i] 则是 sa 的逆运算, 表示起始于位置 \(i\) 的后缀的排名。通过倍增算法或更高效的 DC3/SA-IS 算法, 可以在 \(O(N \log N)\)\(O(N)\) 时间内构建后缀数组。结合 LCP 数组(相邻排名后缀的最长公共前缀), 后缀数组可以解决海量的字符串问题, 如不同子串个数、最长公共子串等。
  • 简单字符串理论 (Lyndon Word, Periodicity Lemma): 这些是字符串学的理论基石。Lyndon 串是一个字典序严格小于其所有非平凡循环同构串的字符串。任何字符串都可以唯一地分解为 Lyndon 串的非增序列 (Chen-Fox-Lyndon 定理)。周期性引理 (Fine-Wilf Theorem) 指出, 如果一个字符串同时有周期 \(p\)\(q\) , 且其长度至少为 \(p + q - \gcd(p, q)\) , 那么它一定有周期 \(\gcd(p, q)\) 。这些深刻的理论虽然不直接对应于某个模板算法, 但理解它们有助于从更本质的层面看待字符串的结构, 有时能启发对复杂问题的巧妙解法。

18. 分块数据结构:优雅的暴力

分块是一种思想,旨在平衡预处理和查询的复杂度,通过将数据分段处理,达到根号级别的复杂度,是一种“优雅的暴力”。

  • 分块 (Blocking): 其核心思想是将一个长度为 \(N\) 的序列划分为 \(\sqrt{N}\) 个长度为 \(\sqrt{N}\) 的块。对于查询操作, 如果它完全覆盖了某些块, 我们可以直接利用预处理好的块信息; 对于不完整的块 (即查询区间的两端所在的块), 则直接暴力遍历。修改操作也类似, 对完整块打上标记, 对不完整块暴力修改。这样, 每次操作最多涉及两个不完整的块和 \(\sqrt{N}\) 个完整的块, 复杂度为 \(O(\sqrt{N})\) 。分块的灵活性极高, 可以支持各种复杂的操作, 只要块信息可以被快速合并和更新。
  • 莫队算法 (Mo's Algorithm): 这是一种专门处理离线区间查询问题的“黑科技”。它将所有查询按特定的方式排序, 然后用一个“移动的”窗口 \([L, R]\) 来回答所有查询。排序的规则是: 将查询按左端点所在块的编号排序, 如果块编号相同, 则按右端点排序。这样排序后, 可以证明窗口的两个指针 \(L\)\(R\) 在整个过程中的总移动次数是 \(O(N \sqrt{N})\) 。只要我们能实现 \(O(1)\) 或近似 \(O(1)\) 地在窗口中增加或删除一个元素, 并更新答案, 总复杂度就是 \(O(N \sqrt{N})\)
  • 莫队二次离线 (Second-order Offline Mo's Algorithm): 对普通莫队算法的扩展, 用于解决移动指针时更新答案的复杂度不是 \(O(1)\) 的情况。具体来说, 当移动指针 (例如, 将 \(R\) 移动到 \(R + 1\) ) 时, 需要计算新加入元素 \(a_{R+1}\) 对当前区间 \([L, R]\) 的贡献, 这个贡献可能是一个需要 \(O(\sqrt{N})\) 甚至更长时间计算的查询。二次离线将这些指针移动产生的贡献查询再次离线下来, 通过差分和预处理, 用更高效的方式(如数论分块、数据结构)批量计算这些贡献, 最终将单次指针移动的均摊代价降回 \(O(1)\)\(O(\log N)\) 级别。

19. 特定模型的计数:群论与组合之美

这些是组合计数中更专门化、更深刻的领域,往往与代数结构和高级组合技巧相关。

  • Burnside 引理 & Polya 枚举定理:这两个是解决带对称性计数问题的强大工具。Burnside 引理给出了在一个置换群 \(G\) 作用下,一个集合 \(X\) 的等价类(轨道)数目。其公式为:轨道数 \(= \frac{1}{|G|} \sum_{g \in G} |X^g|\) ,其中 \(|X^g|\) 是在置换 \(g\) 下保持不变的元素的数量。Polya 枚举定理是 Burnside 引理的推广,它不仅能计数,还能对不同类型的方案进行具体计数(例如,用 \(k\) 种颜色对一个正方体染色,问每种颜色用量构成的不同方案数)。它引入了循环指数多项式,使得计数更加精细。

  • BEST定理:用于计算有向图 \(G\) 中欧拉回路的数量。欧拉回路是指遍历图中每条边恰好一次的闭合路径。BEST定理指出,欧拉回路的数量为:

\(t_w(G) \cdot \prod_{v \in V} (\deg^{-}(v) - 1)\)

其中 \(t_w(G)\) 是以任意顶点 \(w\) 为根的、根向树形图(arborescence)的数量。根向树形图的数量可以通过矩阵树定理 (Matrix Tree Theorem) 计算,即计算图的基尔霍夫矩阵(度数矩阵减去邻接矩阵)的任意一个代数余子式。BEST 定理将一个动态的路径计数问题转化为了一个静态的树计数问题。

·*钩子公式(Hook-Length Formula):这是一个计算标准杨氏表(Standard Young Tableaux)数量的惊人公式。一个形状为入的标准杨氏表,是将数字1到n填入对应的杨氏图的格子中,使得每行每列都递增的方案数。钩子公式表明,方案数f为:

\(f^{\lambda} = \frac{n!}{\prod_{\lambda \in \mathcal{A}} n!(\lambda)}\)

其中,乘积遍历杨氏图中的所有格子 \(x\) ,而 \(h_{\lambda}(x)\) 是格子 \(x\) 的“钩子长度”,即格子 \(x\) 本身,加上它正下方和正右方的格子总数。这个公式在表示论和对称函数理论中有深刻背景,是组合数学中最优雅的公式之一。

20. 经典构造交互模型:思维的棋局

在掌握了基础的思考方向后,我们可以总结一些在构造和交互问题中反复出现的经典技巧和模式。

  • 二进制分组/位贡献:这是处理构造和交互问题最常见的技巧之一。当问题与数字、集合或选择有关时,可以按二进制位来思考。例如,构造一个集合使其满足某些异或和性质,可以考虑每个二进制位上的贡献。在交互题中,可以通过查询某二进制位为1的所有元素的某种性质,来确定答案在该位上的值。这种方法将一个复杂的问题分解为 \(\log C\) 个独立的、更简单的子问题。
  • 随机化与概率方法:当确定性算法难以设计或过于复杂时,随机化可以成为一个有力的武器。例如,在一些构造问题中,可以随机生成一个解,然后通过局部调整来修复不满足条件的部分。在交互题中,随机选择查询对象可以有效对抗对手的最坏情况策略。概率方法(Probabilistic Method)则是一种非构造性的证明技巧:要证明一个满足某种性质的对象存在,只需证明随机构造一个对象时,它满足该性质的概率大于0。
  • 归纳构造与分治:许多构造问题的解具有递归结构。尝试将规模为 \(N\) 的问题的解,通过规模为 \(N - 1\)\(N - 2\)\(N / 2\) 的解来构建。例如,格雷码的构造,就是通过将 \(N - 1\) 位的格雷码序列复制、翻转、并分别加上前缀0和1得到的。分治思想也常用于构造,将一个大问题分解为两个独立的子问题,分别构造解,然后想办法将两个子问题的解“拼接”起来。这种“分而治之,合而为一”的策略是解决复杂构造问题的关键。

21. 数论函数与筛法(二)

莫比乌斯反演(Mobius Inversion)

莫比乌斯反演是数论中一种重要的“容斥”工具,它揭示了定义在偏序集(在算法竞赛中,通常是整除关系)上的两个函数之间的深刻联系。其基础是莫比乌斯函数 \(\mu(n)\)

\(n = 1\)\(\mu (1) = 1\)

  • \(n\) 有平方因子, \(\mu(n) = 0\)
  • \(n\)\(k\) 个不同素数的乘积, \(\mu(n) = (-1)^{k}\)

反演有两种基本形式。第一种是狄利克雷卷积形式:

设有两个数论函数 \(f(n)\)\(F(n)\) ,如果它们满足 \(F(n) = \sum_{d \mid n} f(d)\) ,那么我们有:

\(f(n) = \sum_{d\mid n}\mu (d)F\left(\frac{n}{d}\right)\)

这个公式的本质是将一个函数的“和”形式还原为它的“点值”形式。在竞赛中,它常被用来处理与gcd相关的问题,例如将 \(\sum \sum [\gcd (i,j) = k]\) 的形式通过变换转化为 \(\sum \sum \sum_{d|\gcd (i,j)}\mu (d)\) 的形式,从而简化求和。

第二种形式在处理区间问题时更为常见:

\(F(n) = \sum_{k=1}^{\lfloor N / n\rfloor} f(kn)\) ,则有 \(f(n) = \sum_{k=1}^{\lfloor N / n\rfloor} \mu(k) F(kn)\)

掌握莫比乌斯反演的关键在于识别题目中隐藏的 \(F(n) = \sum_{d\mid n}f(d)\) 结构,并熟练运用它进行求和变量的代换与化简。

杜敦筛 (Du'sSieve)

杜教筛是一种在亚线性时间复杂度(通常是 \(O(n^{2/3})\) )内计算数论函数前缀和的算法。它并非万能,但对于特定类型的函数非常高效。设我们要计算 \(S(n) = \sum_{i=1}^{n} f(i)\)

杜教筛的核心思想是构造另一个数论函数 \(g(n)\) ,使得 \(f\)\(g\) 的狄利克雷卷积

\((f*g)(n) = \sum_{d\mid n}f(d)g(n / d)\) 的前缀和能够快速计算。

我们从这个卷积的前缀和入手:

\(\sum_{i=1}^{n}(f*g)(i) = \sum_{i=1}^{n}\sum_{d|i}f(d)g\left(\frac{i}{d}\right) = \sum_{j=1}^{n}g(j)\sum_{d=1}^{\lfloor n/j\rfloor}f(d) = \sum_{j=1}^{n}g(j)S\left(\left\lfloor \frac{n}{j}\right\rfloor\right)\)

\(j = 1\) 的项分离出来,我们得到

\(g(1)S(n) = \sum_{i = 1}^{n}(f*g)(i) - \sum_{j = 2}^{n}g(j)S\left(\left\lfloor \frac{n}{j}\right\rfloor\right)\)

这个公式是杜教筛的根基。如果我们能快速计算 \(\sum (f*g)\)\(\sum g\) 的前缀和,就可以通过递归和数论分块来计算 \(S(n)\) 。通常,我们会预处理 \(n\) 的前 \(n^{2 / 3}\) 个值,对于更大的 \(n\) ,使用上述公式并用哈希表或std::map记忆化。

例如,求 \(\sum \mu (i)\) ,我们知道 \(\mu *I = \epsilon\) (其中 \(I(n) = 1\) 是恒等函数, \(\epsilon (n) = [n = 1]\) 是单位元),它们的前缀和都极易计算,因此杜教筛是完美的选择。

PN筛(PowerfulNumberSieve)

PN 筛,又称 Powerful Number 筛,是另一种亚线性求积性函数前缀和的算法。它适用的场景与杜教筛略有不同。一个数 \(n\) 被称为 Powerful Number,如果它的所有素因子次数都大于等于 2。 \(n\) 以内的 Powerful Number 数量级为 \(O(\sqrt{n})\)

PN筛的核心思想是:对于一个积性函数 \(f\) ,我们尝试找到另一个“更好算”的积性函数 \(g\) ,使得 \(f = g * h\) (狄利克雷卷积),并且 \(h\) 在非Powerful Number处的值为0。如果能做到这一点,那么 \(\sum f\) 就可以表示为:

\(\sum_{i = 1}^{n}f(i) = \sum_{i = 1}^{n}(g*h)(i) = \sum_{i = 1}^{n}\sum_{d\mid i}g(d)h\left(\frac{i}{d}\right) = \sum_{d = 1}^{n}h(d)\sum_{k = 1}^{\lfloor n / d\rfloor}g(k)\)

由于 \(h(d)\) 只在 \(d\) 是Powerful Number时非零,我们可以只枚举这些 \(d\) 。如果我们能快速计算的前缀和,问题就解决了。通常,我们构造使得在素数 \(p\)\(g(p) = f(p)\) 。这样构造出的满足 \(h(p) = 0\) ,从而保证了 \(h\) 仅在Powerful Number处非零。

PN 筛的威力在于,它将对 \(f\) 的求和转化为了对 \(g\) 的求和以及对 \(O(\sqrt{n})\) 个 Powerful Number 的值的计算。

Min_25筛

Min_25 筛是目前解决积性函数前缀和问题最强大的工具之一,适用范围比杜教筛和 PN 筛更广。它适用于一类积性函数 \(f(n)\) ,要求 \(f(p^k)\) 能够快速计算,并且 \(f(p)\)\(p\) 为素数)可以表示为关于 \(p\) 的低次多项式。

Min_25 筛分为两个主要步骤:

1.计算素数处的函数值之和:定义 \(g(n,j) = \sum_{i = 1}^{n}[i\in \mathbb{P}\text{or}\mathrm{lpf}(i) > p_j]f(i)\) ,其中 \(\mathbb{P}\) 是素数集合, \(\operatorname {lpf}(i)\)\(i\) 的最小质因子, \(p_j\) 是第 \(j\) 个素数。在实践中,我们通常是对 \(p^k\) 分别求和,即计算 \(\sum_{i\in \mathbb{P},i\leq m}i^k\) 这一步通过一个类似埃氏筛的DP过程完成,从所有数开始,逐步筛掉以小素数为最小质因子的合数。 \(g(n,j) = g(n,j - 1) - p_j^k\left(g\left(\left|\frac{n}{p_j}\right|,j - 1\right) - g(p_{j - 1},j - 1)\right)\)

2.计算所有数的函数值之和:定义 \(S(n,j) = \sum_{i = 1,\mathrm{lp}(i)\geq p_j}^n f(i).\) 我们递归地计算它。 \(S(n,j)\) 的贡献分为两部分:一部分是最小质因子大于 \(p_j\) 的素数(这部分的结果由第一步给出),另一部分是最小质因子等于 \(p_k(k\geq j)\) 的合数。 \(S(n,j) = g(n,|P|) - \sum_{k = 1}^{j - 1}f(p_k) + \sum_{k = j,p_k^2\leq n}\sum_{e = 1,p_k^{e + 1}\leq n}f(p_k^e)S\left(\left[\frac{n}{p_k}\right],k + 1\right) + f(p_k^{e + 1})\)

(注意:这里的递推式有多种写法,核心思想是按最小质因子分类讨论)。最终答案是 \(S(n,1) + f(1)\) 。Min_25筛的复杂度为 \(O(\frac{n^{3/4}}{\log n})\) 或更优的 \(O(n^{1-\epsilon})\)

22.凸优化相关

在算法竞赛中,“凸优化”通常指一类特定形式的优化问题,其目标函数具有凸性,从而允许我们使用高效的算法如三分法或更具技巧性的WQS二分来求解。

凸优化与wqs二分

凸性:一个定义在某个区间上的函数 \(f(x)\) ,如果其二阶导数 \(f''(x) \geq 0\) ,则称其为凸函数。在离散问题中,我们更关心其差分性质:对于一个序列 \(a_i\) ,如果其差分序列 \(a_i - a_{i-1}\) 是单调不减的,我们称这个序列(或其背后的函数)具有凸性。直观上,函数的“增长速度”越来越快。如果求最大值,我们则关心凹性(差分单调不增)。这种单调性是应用三分法(Ternary Search)的前提。当我们需要在一个凸函数上求最值时,可以直接在定义域上进行三分查找。

WQS二分(又称Alien's Trick或带权二分):这是一种处理“恰好选取 \(k\) 个物品”这类约束的强大技巧。假设我们有一个问题,要求我们选择一些物品以最大化收益,但有一个限制是必须恰好选择 \(k\) 个。如果去掉这个限制,问题会变得简单很多。设不受限制时的最优收益是 \(f(x)\) ,其中 \(x\) 是选择的物品数量。如果函数 \(f(x)\) 是一个上凸函数(即收益的边际效应递减),我们就可以使用WQS二分。

其核心思想是引入一个“惩罚”或“奖励” \(\lambda\) 。我们将问题转化为:每选择一个物品,除了它本身的价值外,还需付出 \(\lambda\) 的代价(或者获得 \(\lambda\) 的奖励)。我们最大化的目标函数变为 \(value - k \cdot \lambda\) 。这个新问题没有数量限制,通常可以用贪心或DP简单解决。令 \(g(\lambda)\) 为新问题最优解中选择的物品数量。由于 \(f(x)\) 是凸的,可以证明 \(g(\lambda)\) 关于 \(\lambda\) 是单调不增的。因此,我们可以二分查找一个合适的 \(\lambda\) ,使得 \(g(\lambda)\) 恰好等于 \(k\)

最终的答案是:在最优的 \(\lambda^{*}\) 下求得的最大价值,再加上 \(k\cdot \lambda^{*}\)

需要注意处理小()存在“平台期”的情况,即多个不同的对象相对应的选择数量。此时,我们需要在最优解中选择恰好4个物品,通常是选择那些价值等于 \(\lambda^*\) 的“临界”物品。

23. 其它方法

在特定问题中极为有效的思想和技巧。

■ 邻项递推

邻项递推是一种优化动态规划的思路,尤其适用于状态转移仅与前一状态相关的场景。很多DP问题,其状态转移方程看似复杂,例如 \(dp_{i} = \max_{0\leqslant j < 1}\{f(d_{p_j},i)\}\) 。但通过代数变形,有时可以将其化为只与 \(dp_{i - 1}\) 相关的形式。

一个经典的应用场景是斜率优化DP,在某些问题中,状态转移方程可以写成 \(dp_{i} = \max_{i < j}\{a_{i},x_{j} + y_{j}\}\) 的形式。如果 \(a_{i}\) 单调,我们可以用单调队列维护凸包来优化。但如果 \(a_{i}\) 不单调,问题会变得复杂。此时,可以观察决策点是否具有某种单调性。比如,对于 \(i\) ,最优决策点是 \(p_i\) ,那么对于 \(i + 1\) ,其最优决策点 \(p_i + 1\) 很可能满足 \(p_{i + 1}\geq p_i\) (决策单调性)。即使没有决策单调性,有时通过对 \(dp_{i}\)\(dp_{i - 1}\) 的关系进行分析,我们可以发现 \(dp_{i}\) 的值可以通过 \(dp_{i - 1}\) 的值经过一个相对简单的函数变换得到。这种从 \(i - 1\) 到的递推关系,一旦被发现,就能将复杂的 \(O(n^{2})\) DP优化到 \(O(n)\)\(O(n\log n)\) 。这需要敏锐的数学观察力和代数变形能力。

类欧几里得算法 (Like-Euclidean Algorithm)

类欧几里得算法是用来解决一类特定求和问题的强力工具,这些问题通常形如:

\(f(a,b,c,n) = \sum_{i = 0}^{n}\left\lfloor \frac{a + b}{c}\right\rfloor\)

以及它的两个变种:

\(g(a,b,c,n) = \sum_{i = 0}^{n}i\left|\frac{a + b}{c}\right|\)

h(a,b,c,n)=∑n a+b

算法的核心思想类似于欧几里得算法求最大公约数的过程,通过递归降低参数的规模。

\(a \geq c\)\(b > c\) 时,我们可以把 \(|a / c|\)\(|b / c|\) 提出,问题规模不变,但 \(a, b\) 变小:

\(\sum_{i = 0}^{n}\left|\frac{a + b}{i}\right| = \sum_{i = 0}^{n}\left(\left|\frac{(a(\mathrm{mod}c))i + (b(\mathrm{mod}c))}{i}\right| + i\right|\frac{a}{i} +\left|\frac{b}{i}\right|\right)\)

这部分可以 \(O(1)\) 计算

\(a < c\)\(b < c\) 时,是算法的精髓。我们令 \(m = \left|\frac{a + b}{c}\right|\) ,然后交换求和顺序,将对i的求和转化

为对 \(\left|\frac{a + b}{2}\right|\) 的值的求和,通过一系列精妙的代数变换,原问题 \(f(a, b, c, n)\) 可以转化为一个新的

参数规模更小的 \(f(c, c - b - 1, a, m - 1)\) 问题。由于参数 \(a\) , \(c\) 的角色发生了类似欧几里得算法的交换,整个算法的复杂度是 \(O(\log (\max(a, c)))\)

万能欧几里得(GeneralEuclideanAlgorithm)

万能欧几里得是类欧几里得算法的终极推广。它不再局限于计算那三种特定的和式,而是解决一类与“直线路径”相关的计数问题。想象在二维平面上,一条直线 \(y = (ax + b) / c\)\(x = 0\) 走到 \(x = n\) 。这条路经会穿过一系列的单元格。我们可以用一个字符串来描述它的路径,例如R代表向右走一步(增加1),U代表向上走一步(跨过一条水平线 \(y = k\) )。类欧几里得计算的就是U的总数等信息。

万能欧几里得的思想是,我们将 \(\mathbb{R}\) 和U视为某种操作,这些操作构成一个幺半群(Monoid)。例如,操作可以是矩阵乘法,或者其它满足结合律的运算。我们要计算的是从 \((0, [b/c])\) 开始,按顺序执行 \(n^{\times}n^{\times}\) 走一段,然后向上取整“这个宏操作后,得到的累积变换结果。这个宏操作序列可以表示为类似 \((R^{k_{1}}UR^{k_{2}}U\ldots)\) 的形式。万能欧几里得算法利用和类欧几里得算法一样的递归结构,将一个形如 \((A^{k}B)^{r}\) 的长操作序列,递归地分解和计算。它提供了一个模板,你只需要定义你的操作(状态)以及 \(\mathbb{R}\) 和U分别对应何种状态转移,就可以在 \(O(\log n)\) 的时间内解决一大类几何计数问题。

24. 无向图连通性

图的连通性是图论的基石。

DFS树,Tarjan算法

在无向图上进行深度优先搜索(DFS),会形成一棵DFS树(或森林)。图中的边被分为两类:树边(DFS过程中实际经过的边)和返祖边(或称回边,连接一个节点到其在DFS树中祖先的边)。无向图中不存在其它类型的边(如横叉边或前向边)。这个简单的分类是Tarjan系列算法的理论基础。

Tarjan算法是一种基于DFS和DFS树思想的算法,用于高效地寻找图中的割点、桥和各种连通分量。其核心是维护两个数组:

  • \(dfn[u]\) :节点 \(u\) 在DFS中被访问到的时间戳(或顺序)。

  • low[u]:从节点u出发,通过DFS树的树边,以及至多一条返祖边,能够到达的dfn值最小的节点的dfn

值。

low[u]的计算公式为:

\(low[u] = \min (dfn[u],\min_{v\text{is ancestor}}\{dfn[v]|(u,v)\text{is a back edge}\} ,\min_{v\text{is child}}\{low[v]|(u,v)\)

通过比较dfn和low值,我们可以判断流通性:

·桥(Bridge):条树边 \((u,v)\)\(v\)\(u\) 的儿子)是桥,当且仅当 \(low[v] > dfn[u]\) 。这意味着从 \(v\) 子树出

发,无法通过返祖边回到 \(u\)\(u\) 的祖先。

  • 割点 (Cut Vertex) : 一个节点 \(u\) 是割点
  1. \(u\) 是 DFS 树的根,且有两个或以上儿子。

  2. \(u\) 不是根,且存在一个儿子 \(v\) ,使得 \(low[v] \geq dfn[u]\) 。这意味着去掉 \(u\) 后, \(v\) 子树就与 \(u\) 的父节点

分离了。

Tarjan算法的优雅之处在于,它仅需一次DFS就能计算出所有关键信息,复杂度为线性时间 \(O(V + E)\)

双连通分量(Biconnected Components)

双连通分量是衡量图“冗余度”或“鲁棒性”的概念,分为边双连通分量和点双连通分量。

  • 边边连通分量(Edge Biconnected Component,e-BCC):一个极大的子图,其中任意两点之间都至少有两条边不相交的路径。等价地,一个e-BCC中不包含任何桥。我们可以通过求出图中所有的桥,然后将桥断开,剩下的每个连通块就是一个边边连通分量。

  • 点双连通分量 (Vertex Biconnected Component, v-BCC): 一个极大的子图, 其中任意两点之间都至少有两个点不相交的路径 (除了起点和终点)。等价地, 一个 v-BCC 是一个没有割点的子图, 或者是一条边和它的两个端点。注意, 一个割点可以属于多个 v-BCC。v-BCC 的求解也依赖 Tarjan 算法。在找到割点时, 与该割点相关的组件可以被区别并从栈中弹出。

Menger定理(Menger'sTheorem)

Menger定理是网络流中最大流最小割定理在普通图论中的对应版本,是组合数学中的一个基石性定理。它深刻地揭示了路径不相交性与分割集大小之间的对偶关系。

定理分为点连通和边连通两个版本

  • 边连通形式:对于图中任意两个不同顶点 \(u, v\) ,能够找到的边不相交的 \(u - v\) 路径的最大数量,等于分离 \(u\)\(v\) 所需要移除的最小边数( \(u - v\) 边割)。

  • 点连通形式:对于图中任意两个相邻的顶点 \(u, v\) ,能够找到的内部点不相关的 \(u - v\) 路径的最大数量,等于分离 \(u\)\(v\) 所需要转移的最小顶点数( \(v - u\) 占点)。

Menger定理为双连通分量的定义提供了理论依据,并且是许多网络设计和可靠性分析问题的理论基础。虽然在竞赛中我们不常直接实现Menger定理的证明过程,但理解其思想对于建立图连通性的直觉至关重要。

边双缩点与点双缩点(圆方树)

对图进行“缩点”是将一个连通分量(或其它等价类)看作一个单一的“超级节点”,从而简化图的结构。

  • 边双缩点(e-BCC Shrinking):将每个边双连通分量缩成一个点。由于 e-BCC 之间由桥连接,缩点后的图必然是一棵树(或森林)。这个操作非常有用,因为它将一个复杂的图问题转化为了一个简单的树上问题。例如,在图中两点间增加一条边,最少需要再增加几条边才能使整个图边双连通?这类问题在缩点后的树上就变得直观了

  • 点双缩点(V-BCC Shrinking)与圆方树(Block-Cut Tree / Round-Square Tree):点双的缩点稍微复杂,因为割点可以属于多个点双。为了处理这个问题,我们引入了圆方树。圆方树是一种新的圆结构,包含两种类型的节点:

·圆点(Bound Node):代表原图中的顶点

·方点(Square Node):代表原图中的一个点双连通分量。

在圆方树中,一个圆点和一个方点B相连,当且仅当原图中的顶点属于点双连通分量B。

圆方树的美妙之处在于它也是一棵树。它精巧地保留了原图中所有点双连通分量和割点之间的邻接关

系。例如,查询原图中两点u、之间的所有简单路径经过的必经点,就等价于查询圆方树上u、v路径上

经过的所有圆点(不含u,v)。这使得许多复杂的路径问题和仙人掌图问题得以在树形结构上解决。

25. 高阶字符串算法:SAM

后缀自动机(Suffix Automaton, SAM)是处理字符串子串问题的终极利器。对于一个字符串 \(S\) ,它的 SAM 是一个接受 \(S\) 所有后缀的、状态数最少的确定性有限自动机(DFA)。其精妙之处在于,这个最小的 DFA 也恰好包含了 \(S\) 的所有子串信息。

SAM的核心结构由状态和转移组成。每个状态代表了一个或多个子串的集合,这些子串在原串 \(S\) 中拥有完全相同的结束位置集合(endpos set)。SAM的关键性质如下:

  1. 它是一个有向无环图(DAG)。从初始状态出发的任意一条路径,都对应 \(S\) 的一个唯一子串。
  2. 它有一个名为 parent 或 link 的指针结构,构成了所谓的“parent 树”。一个状态 u 的 link 指针指向另一个状态 v,其中 v 对应的子串是 u 对应的最长子串的那个最长的后缀。parent 树揭示了子串间的后缀关系。
  3. 每个状态 u 还有一个属性 len[u],表示该状态所能代表的最长子串的长度。

SAM 可以在 \(O(|S|)\) 的时间内在线性构造。构造过程是逐个添加字符,并维护自动机的结构。每添加一个新字符 c,我们为新字符串创建一个新状态,然后从代表整个旧字符串的状态开始,沿着 parent 树向上跳,为路径上的状态添加到新状态的 c 转移。这个过程中可能会涉及状态的“分裂”,以维持 endpos 集合的性质,但均摊分析表明整个构造是线性的。

SAM的应用极其广泛,包括但不限于:

  • \(O(|S|)\) 计算不同子串数量。
    \(\cdot O(|P|)\) 判断模式串 \(P\) 是否是 \(S\) 的子串。
  • \(O(|S|)\) 计算所有子串的总长度。
  • 解决两个或多个字符串的最长公共子串(LCS)问题。
  • 计算第 \(\mathrm{k}\) 小的不同子串。

掌握SAM的关键在于理解其parent树结构和DAG结构,并能结合具体问题在这两种结构上进行动态规划或树形计算。

26.幂级数

幂级数,特别是生成函数,是将组合计数问题代数化的强大语言。它将离散的数列与连续的函数联系起来,使得我们可以用微积分和代数学的工具来解决组合问题。

生成函数 (Generating Functions)

生成函数是幂级数在组合数学中的核心应用。一个序列 \(A = \{a_0, a_1, a_2, \ldots\}\) 可以被一个形式幂级数 \(A(x) = \sum_{i=0}^{\infty} a_i x^i\) 所代表。这个 \(x\) 通常不取具体值,而是作为各项的占位符。

  • 普通生成函数 (OGF): \(A(x) = \sum a_{n}x^{n}\) 。适用于对无标号对象的组合计数。例如, 从无限多种物品中选取 \(n\) 个的方案数, 其生成函数为 \((1 + x + x^{2} + \ldots)^{k} = \left(\frac{1}{1 - x}\right)^{k}\) 。两个 OGF 的乘积 \(C(x) = A(x)B(x)\) 对应着卷积 \(c_{n} = \sum_{i=0}^{n}a_{i}b_{n-i}\) , 这在组合意义上代表将两种结构合并。
  • 指数生成函数(EGF): \(A(x) = \sum \frac{a_n}{n!} x^n\) 。适用于对有标号对象的组合计数。EGF的乘积 \(C(x) = A(x)B(x)\) 对应着二项卷积 \(c_n = \sum_{i=0}^{n} \binom{n}{i} a_i b_{n-i}\) ,这正好是把 \(n\) 个有标号对象分成两部分,一部分按 \(A\) 的方式排列,另一部分按 \(B\) 的方式排列的方案数。

泰勒展开 (Taylor Expansion)

泰勒展开是连接函数分析与生成函数的桥梁。许多我们熟悉的函数,如 \(e^x, \ln(1 - x), (1 + x)^{\alpha}\) ,它们的泰勒级数展开式恰好对应着一些基本组合对象的生成函数。例如:

\(\cdot e^{x} = \sum_{n = 0}^{\infty}\frac{x^{n}}{n!}\) 是所有排列的指数生成函数。
\(\frac{1}{1 - x} = \sum_{n=0}^{\infty} x^{n}\) 是“允许重复选择”这一最基本组合问题的普通生成函数。

在竞赛中, 我们常常先通过组合意义构建出一个关于生成函数的方程, 然后解出这个方程得到一个封闭形式的函数, 最后通过泰勒展开 (或查表) 将其展开成幂级数, 从而读出我们想要的系数 \(a_{n}\)

\(\mid^{*}\mathbf{n}^{\wedge}2\) 多项式操作

在现代算法竞赛中,多项式操作通常指基于快速傅里叶变换(FFT)或数论变换(NTT)的 \(O(n\log n)\) 算法。然而,在多项式次数 \(n\) 较小,或者模数不满足NTT要求时,朴素的 \(O(n^{2})\) 算法仍然有一席之地。

·加/减法: \(O(n)\) ,对应项相加减
·乘法(卷积): \(C(x) = A(x)B(x)\) ,则 \(c_{k} = \sum_{i = 0}^{k}a_{i}b_{k - i}\) 。通过两层循环计算,复杂度为 \(O(n^{2})\)

  • 求逆/开方/对数/指数:这些高级操作也可以在 \(O(n^{2})\) 时间内通过递推或牛顿迭代法实现。例如,求 \(A(x)\) 的逆 \(B(x)\) ,即 \(A(x)B(x) \equiv 1 \pmod{x^n}\) 。我们可以从 \(b_{0} = 1 / a_{0}\) 开始,通过 \(A(x)B(x) = 1\) 的系数关系 \(a_{0}b_{k} + \sum_{i=1}^{k} a_{i}b_{k-i} = 0\) 逐项递推出 \(b_{k}\)

拉格朗日插值 (Lagrange Interpolation)

拉格朗日插值是一种求多项式的方法。给定 \(n + 1\) 个不同的点 \((x_0, y_0), (x_1, y_1), \ldots, (x_n, y_n)\) ,存在唯一一个次数不超过 \(n\) 的多项式 \(P(x)\) 经过所有这些点。拉格朗日插值给出了这个多项式的显式构造:

\[P (x) = \sum_ {i = 0} ^ {n} y _ {i} L _ {i} (x) \quad \text {其 中} \quad L _ {i} (x) = \prod_ {j = 0, j \neq i} ^ {n} \frac {x - x _ {j}}{x _ {i} - x _ {j}} \]

\(L_{i}(x)\) 是基函数,它在 \(x_{i}\) 处为1,在所有其他 \(x_{j}(j\neq i)\) 处为0。

在算法竞赛中,它常用于以下场景:当你知道某个问题的答案 \(f(k)\) 是一个关于 \(k\) 的低次(比如 \(d\) 次)多项式,但你无法直接求出多项式的系数。此时,你可以暴力计算出 \(f(0), f(1), \ldots, f(d)\)\(d + 1\) 个点的值,然后使用拉格朗日插值在 \(O(d^{2})\) 时间内求出 \(f(k)\) 对于某个非常大的 \(k\) 的值。对于求特定点值,这比高斯消元求系数再求值要快。

27. 二分图

二分图是图论中一类结构简单但应用极其广泛的图,其顶点可以被划分为两个不相交的集合 \(U\)\(V\) ,使得所有边都连接 \(U\)\(V\) 中的顶点。

最大匹配,最大独立集,最小点覆盖

这三个概念在二分图中有着深刻的对偶关系。

  • 最大匹配 (Maximum Matching):图中边数最多的、任意两条边都没有公共顶点的边集。求解算法主要是基于增广路的匈牙利算法(Hopcroft-Karp 是其更快的版本)。增广路是一条起点和终点都未被匹配,且交替出现非匹配边和匹配边的路径。找到一条增广路,将其上的边状态翻转,匹配数就能加一。
  • 最小点覆盖 (Minimum Vertex Cover): 用最少的点覆盖住所有的边(每条边至少有一个端点被选中)。
  • 最大独立集 (Maximum Independent Set):选出最多的点,使得任意两点之间都没有边。

König定理是连接这三者的桥梁:在任意二分图中,最大匹配数 \(=\) 最小点覆盖数。

同时,对于任何图,我们都有最大独立集大小 + 最小点覆盖大小 = 顶点总数

结合这两个定理,我们得到二分图中的重要结论:最大独立集大小 \(=\) 顶点总数-最大匹配数。这使得我们可以通过求最大匹配来解决这三个问题。

Hall定理(Hall'sTheorem)

Hall定理给出了二分图存在完美匹配(或覆盖一侧所有点的匹配)的充要条件,是一个理论性工具。对于一个二分图 \(G = (U\cup V,E)\) ,存在一个能覆盖 \(U\) 中所有顶点的匹配,当且仅当对于 \(U\) 的任意子集 \(S\subseteq U\) ,其邻居集合 \(N(S)\subseteq V\) 的大小满足 \(|N(S)|\geq |S|\) 。这个“婚姻定理”直观地说明,任何 \(k\) 个来自 \(U\) 的顶点,它们必须总共至少有 \(k\) 个不同的邻居,才有可能给它们都找到配偶。在竞赛中,它多用于证明和问题分析,而不是直接作为算法。

最大权完美匹配 (KM 算法)

当二分图的边带权时,我们可能希望找到一个完美匹配(即 \(U, V\) 顶点数相等且所有点都被匹

配),使得匹配边的权值之和最大。这就是最大权完美匹配问题。

Kuhn-Munkres (KM) 算法是解决此问题的经典方法。它通过为每个顶点赋予一个“顶标”(见图 1.2.1):一个顶标可表示为 \(x_{i} = x_{j}\) 的多项式(见图 1.2.2)。

(1abel) \(v(v)\) 来工作。一个可行的贝塔方案满足对于任意边 \((u,v)\) 都有 \(\ell(u) + \ell(v) \geq w(u,v)\)\(K\lambda\) 算法在中消去 \(l(u), l(v) = w(u,v)\) 的决构成的“相等子图”中寻找增亡路,如果找不到,则

RW算法在由满足(2)-(3)-(4)的递归的格子图中自顶向下构造产品。如果找不到,它会继续系统地修改顶标,使得更多的边进入相等子图,同时保持顶标的可行性,直到找到完美匹配为止。

止。这个过程保证了找到的完美匹配一定是权值最大的。其复杂度通常为 \(O(|V|^3)\)

28.字符串技巧

除了KMP、AC自动机、SAM这些大型算法,字符串问题中还有许多灵活多变、依赖于对字符串结构深刻理解的技巧和模型。

回文串模型与Manacher

许多涉及回文串的问题,其核心都是要快速获取所有回文子串的信息。Manacher算法能在 \(O(n)\) 时间内求出以每个字符(以及字符间隙)为中心的最长回文半径。通过在字符间插入特殊符号,它巧妙地将奇数长度和偶数长度的回文串统一处理。算法的核心是利用已经计算出的回文串信息来加速后续计算,即如果当前中心在一个已知的大回文串内部,那么它的回文半径可以由其对称点的半径信息推断出一个下界。一旦有了所有中心的最长回文半径,关于回文子串的计数、查找等问题便迎刃而解。

周期性、Border与KMP的next数组

KMP算法的next(或fail)数组是字符串周期的信息宝库。next[i]定义为字符串前缀s[1..i]的最长真前缀,使得该前缀等于s[1..i]的一个后缀。这个“既是前缀又是后缀”的结构被称为Border。一个字符串s存在长度为L的Border,等价于它存在长度为|s|-L的周期。通过不断访问i,next[i],next[next[i]],...,我们可以得到s[1..i]的所有Border长度,从而揭示其所有的周期性结构。这个性质在处理字符串重复、拼接、覆盖等问题时极为有用,是许多难题的突破口。

后缀数组 (SA) 与 LCP 数组

后缀数组SA和最长公共前缀数组LCP是解决子串问题的另一大杀器,功能与SAM有部分重叠但各有千秋。SA[i]表示将所有后缀排序后,排在第i位的后缀的起始位置。LCP[i]表示排在第 \(i - 1\) 位和第 \(i\) 位的两个后缀的最长公共前缀。这个组合的威力在于:

  1. 任意两后缀的 LCP: LCP(SA[i], SA[j]) 可以通过 RMQ 在 LCP 数组的区间 \([i+1, j]\) (假设 \(i < j\) )上查询最小值得到。

  2. 不同子串计数:总子串数减去重复计算的。重复的子串就是后缀间的公共前缀。总不同子串数为 \(\sum_{i=1}^{n}(n - SA[i] + 1 - LCP[i])\)

  3. 模式匹配:可以在 SA 上二分查找模式串,找到其在排序后缀中的位置范围。

  4. 多串LCS:将所有串用特殊分隔符拼接,建SA和LCP,然后问题转化为在LCP数组上寻找一个区间,其最小值最大,且该区间对应的后缀来自所有不同的原串。

29. 集合幂级数

集合幂级数是将生成函数的思想从整数域推广到集合域的工具,而快速沃尔什-阿达玛变换

(FWT) 则是其背后的“FFT”。它主要用于处理与位运算 (AND, OR, XOR) 相关的卷积问题。

FMT,FWT与集合幂级数

一个集合幂级数是形如 \(F = \sum_{S \subseteq U} f_S \cdot S\) 的形式和,其中 \(U\) 是全集,我们通常用一个长度为 \(2^{|U|}\) 的数组 \(A\) 来表示其系数 \(f_S\) 。FWT 定义了三种主要的集合卷积:

1.OR卷积: \((A*B)_{S} = \sum_{I\cup J = S}A_{I}B_{J}\)
2.AND卷积: \((A*B)_{S} = \sum_{I\cap J = S}A_{I}B_{J}\)

  1. XOR卷积: \((A*B)_{S} = \sum_{I\oplus J = S}A_{I}B_{J}\)

直接计算这些卷积需要 \(O(4^n)\) 的时间 \((n = |U|)\) 。FWT是一种 \(O(n2^n)\) 的变换,它将数组 \(A\) 变换为“点值表示” \(\hat{A}\) ,使得卷积操作变为逐点相乘: \((\widehat{A*B})_S = \hat{A}_S \cdot \hat{B}_S\) 。之后再用逆FWT变换回来。

  • 对于 OR 卷积, 正变换是 \(\hat{A}_S = \sum_{I \in S} A_I\) (子集和)。
  • 对于 AND 卷积,正变换是 \(\hat{A}_S = \sum_{S \subset I} A_I\) (超集和)。
  • XOR卷积的变换则类似于真正的阿达玛变换。

这些变换都可以通过“按维DP”的方式在 \(O(n2^n)\) 内完成。

*子集卷积 (Subset Convolution)

子集卷积是集合卷积中更精确也更复杂的一种,其定义为:

\((A*B)_{S} = \sum_{L\subseteq S}A_{L}B_{S\setminus L}\)

注意这里的条件是 \(L\)\(S \setminus L\) 构成 \(S\) 的一个划分,即 \(L \cup (S \setminus L) = S\)\(L \cap (S \setminus L) = \emptyset\) 。OR卷积只满足第一个条件。

为了处理这个不相交的约束,我们引入一个额外的维度:集合的大小(或称为rank)。我们将系数数组A扩展为二维数组 \(A[k][S]\) ,表示集合S且 \(|S| = k\) 时的系数。子集卷积的定义变为:

\((A*B)_{k,S} = \sum_{i=0}^{k}\sum_{L\subset S,|L|=i}A_{i,L}B_{k-i,S|L}\)

算法流程如下:

  1. 对每个大小 \(i \in [0, n]\) ,对数组 \(A[i]\)\(B[i]\) 分别进行 FWT (OR 卷积版本)。

  2. 对于每个集合掩码 \(S\) ,我们有两个关于大小的多项式 \(\hat{A}[S]\)\(\hat{B}[S]\) 。将它们在大小这一维上进行普通的多项式乘法,得到 \(\hat{C}[k][S] = \sum_{i=0}^{k} \hat{A}[i][S] \cdot \hat{B}[k-i][S]\)

  3. 对每个大小 \(k_{i}\)\(\hat{C}[k]\) 进行逆FWT。

总复杂度为 \(O(n^{2}2^{n})\) 。子集卷积在解决一类要求划分的图论计数问题(如计算图中哈密顿路径数量)时

非常强大。

30.网络流(一)

网络流是组合优化中的一个核心分支,用于研究网络中物质、信息等的流动。它提供了强大的建模能力,能将许多看似无关的问题转化为图上的流问题来解决。

最大流最小割定理

这是网络流理论的基石。对于一个网络(一个带容量的有向图,有源点 \(s\) 和汇点 \(t\) ),从 \(s\)\(t\) 的最大流量,等于该网络中任何一个 \(s - t\) 最小割的容量。一个 \(s - t\) 刻是将顶点集划分为 \(S\)\(T\) ( \(s \in S, t \in T\) ),割的容量是所有从 \(S\) 指向 \(T\) 的边的容量之和。这个定理的深刻之处在于它建立了一个“流动”问题和一个“分割”问题之间的对偶关系。在建模时,如果问题可以描述为“在付出最小代价的情况下将两类东西分开”,那么它很可能是一个最小割问题,可以用最大流算法求解。

最大流 (EK, Dinic)

求解最大流的算法都基于增广路思想,即在残量网络中寻找从 \(s\)\(t\) 的路径,增加流量,直到找不到增广路为止。

  • Edmonds-Karp (EK): 每次用 BFS 寻找一条增广路。由于 BFS 找到的是边数最少的路径,可以证明其复杂度为 \(O(VE^2)\) 。实现简单,但效率较低。
  • Dinic: 目前竞赛中最常用、效率最高的算法之一。它引入了分层图的概念。每次先用 BFS 对残量网络分层,然后在分层图上用 DFS 一次性找出多条增广路。通过“当前弧优化”,避免了对已探索过的死胡同进行重复搜索。其一般图上的复杂度为 \(O(V^2 E)\) ,但在二分图匹配等特殊场景下有更好的 \(O(E \sqrt{V})\) 复杂度。

费用流(SSP,Primal-Dual)

最小费用最大流问题,在每条边上除了容量外还有一个单位流量的费用,目标是在满足最大流(或给定流量)的前提下,使总费用最小。

  • SSP (Successive Shortest Path) 算法: 这是一个贪心策略。每次在残星网络中寻找一条从 \(s\)\(t\) 的费用最短路(而不是边数最少)来增广。如果存在负边权, 需要使用 SPFA; 如果没有, 则可以使用 Dijkstra 算法。为了在多次增广中保持边权非负以使用 Dijkstra, 通常会引入势 (potential) 的概念, 这本质上就是 Primal-Dual 思想的一种体现。

上下界网络流

标准网络流模型的扩展,其中每条边的流量不仅有上界(容量),还有一个下界。

  • 有源汇上下界可行流:问题是否存在一个流满足所有边的上下界约束和流量守恒。解决方法是构造一个新图。先让每条边流过其下界流量,这会导致某些点流入不等于流出。我们建立超级源点 \(S\) 和超级汇点 \(T\) ,用 \(S\) 补偿流入不足的点,用 \(T\) 吸收流出过多的点。在新图上跑最大流,如果所有从 \(S\) 出发的边都满流,则存在可行流。
  • 有源汇上下界最大/最小流:在可行流的基础上,再在原图的残量网络上跑从 \(s\)\(t\) (或 \(t\)\(s\) )的最大流,来增加或减少总流量。

网络流模型

网络流的真正威力在于其建模能力。以下是一些经典模型:

  • 二分图匹配:最大匹配问题可以看作源点向一侧顶点连容量为1的边,另一侧顶点向汇点连容量为1的边,原图中的边容量也为1,求最大流。
    ·多源汇问题:建立超级源/汇点。
  • 点容量限制:将一个点 \(u\) 拆成入点 \(u_{in}\) 和出点 \(u_{out}\) ,连一条边 \((u_{in}, u_{out})\) ,容量为该点的限制。
  • 最小割模型:广泛用于解决“二选一”类型的问题。例如,将一些物品分到两个集合,选择不同集合有不同代价,可以用最小割巧妙建模。将源点 \(s\) 代表一个选择,汇点 \(t\) 代表另一个,割边代表付出的代价。

31.网络流(二)

网络流是组合优化中的一个核心分支,它以图为模型,研究网络中物质、信息或能量的流动。其理论深刻,应用广泛,是解决诸多匹配、调度和指派问题的终极武器。

  • 最大流最小割定理 (Max-Flow Min-Cut Theorem): 这是网络流理论的基石。对于一个流网络(包含源点 \(s\) 、汇点 \(t\) 和带容量的边),从 \(s\) 到的最大流量,等于该网络中 \(s - t\) 是最小割的容量。一个 \(s - t\) 割是将顶点集划分为包含 \(t\)\(S\) 部和包含 \(t\)\(T\) 部,割的答案是所有从 \(S\)\(T\) 的边的答案之和。这个定理直观地揭示了网络的瓶颈(最小割)决定了其最大传输能力(最大流),并通过增广路算法(如 Ford-Fulkerson)将这两者联系起来。

  • Dinic算法:这是在竞赛中求解最大流问题最常用且高效的算法。Dinic算法基于分层图和阻塞流的思想。它在每次增广前,首先通过一次从源点出发的BFS对图进行分层,构造出只包含有用边(即从第i层指向第i+1层的边)的层次图。然后,在层次图上通过一次或多次DFS寻找增广路并推送尽可能多的流量,直到找不到新的增广路为止(形成阻塞流)。这个“分层-增广”的过程会反复进行,直到i和j不再连通。其在一般图上的复杂度为 \(O(V^2 E)\) ,在二分图匹配等特殊场景下则有更优的表现,如 \(O(E\sqrt{V})\)

  • 最小费用最大流(MCMF):MCMF问题在最大流的基础上,为每条边增加了一个“单位费用”,目标是在保证流量最大的前提下,使总费用最小。求解该问题的经典算法是基于增广路的思想,但每次寻找增广路时,不再是找任意路径或最短(边数最少)路径,而是在残留网络中寻找费用最小的路径。这通常使用Bellman-Ford或SPFA算法(因为可能出现负费用边)。每次沿费用最小的路径增广,直到流量达到最大。

  • 网络流建模:网络流算法本身是模板化的,其真正的威力与挑战在于“建模”——将一个看似无关的问题转化为网络流模型。这是创造力的体现。常见的建模技巧包括:

  • 二分图匹配:将二分图两部顶点分别与源汇点相连,中间的边容量为1,求最大流即为最大匹配。

  • 点容量限制:将一个有容量限制的顶点 \(u\) 拆分为入点 \(u_{in}\) 和出点 \(u_{out}\) ,并连接一条容量为点容量的边 \((u_{in}, u_{out})\)

  • 多源多汇:建立一个超级源点 \(S\) 和一个超级汇点 \(T\)\(S\) 向所有源点连容量为无穷的边, \(T\) 从所有汇点引容量为无穷的边。

  • 最小割模型:利用最大流最小割定理,将一些“选择”问题转化为最小割问题。通常,割断一条边代表一种决策或代价,最小割对应着最小代价的决策方案,如项目获利(最大权闭合子图)问题。

  • 模拟费用流:这是一种高级的优化思想,而非一个固定的模板算法。它适用于那些可以被建模成最小费用最大流,但图结构又非常特殊,以至于可以直接模拟寻找并增广费用最短路这一核心过程的问题。其本质是带悔贪心。我们每做一次贪心选择(例如,匹配一对代价最小的点),就向数据结构(通常是优先队列)中加入一个对应的“反悔操作”。这个反悔操作允许我们在未来的决策中撤销当前的选择,从而达到全局最优,这等价于在费用流的残模图中走反向边。

32.生成函数与多项式(Generating Functions &Polynomialse

生成函数与多项式技术将离散的计数问题转化为代数问题,通过快速傅里叶变换(FFT)等算法高效求解,是组合数学领域的“核武器”。

·生成函数(Generating Functions):它将离散数列与形式幂级数联系起来。

  • 普通生成函数 (OGF): 对于数列 \(\{a_{n}\}\) , 其 OGF 为 \(A(x) = \sum_{n=0}^{\infty} a_{n} x^{n}\) 。主要用于处理“组合”或“无标号”对象的计数问题。两个 OGF 的乘积 \(A(x) B(x)\) 对应其系数序列的卷积。

  • 指数生成函数 (EGF): 对于数列 \(\{a_{n}\}\) , 其 EGF 为 \(A(x) = \sum_{n=0}^{\infty} a_{n} \frac{x^{n}}{n!}\) . 主要用于处理“排列”或“带标号”对象的计数问题. 两个 EGF 的乘积对应系数序列的二项卷积.
    ·核心应用:求解递推关系、复杂组合对象计数(如树、图)、以及某些背包问题的方案数。

  • 快速傅里叶变换 (FFT) 与数论变换 (NTT).

  • 卷积:两个序列 \(a\)\(b\) 的卷积 \(c_{k} = \sum_{i=0}^{k} a_{i} b_{k-i}\) ,正是两个多项式乘积的系数。

  • FFT/NTT:是实现多项式乘法(即卷积)的 \(O(N\log N)\) 算法。其核心思想是,多项式的系数表示“和”点值表示“可以快速相互转换。在点值表示下,乘法只需 \(O(N)\) 。FFT利用复数单位根进行变换,存在精度问题。NTT则在有限域(模一个特殊素数)下使用原根替代单位根,无精度误差,是竞赛中的标准实现。

33. 线性代数与线性递推 (Linear Algebra & Linear Recurrence)

线性代数提供了描述和操作高维线性空间的强大语言,广泛应用于解决递推、计数和图论等问题。

  • 高斯消元 (Gaussian Elimination): 求解线性方程组 \(A\vec{x} = \vec{b}\) 的基本算法。通过一系列初等行变换将增广矩阵化为行阶梯形矩阵, 然后回代求解。复杂度为 \(O(N^{3})\) 。它还可以用于求矩阵的逆、矩阵的秩, 以及解决一些期望 DP 问题。

  • 线性基 (Linear Basis): 特指在异或 (XOR) 运算下的向量空间基。给定一个数集 \(S\) , 其线性基是一个最小的集合 \(B\) , 使得 \(S\) 中任何数都可以由 \(B\) 中若干个数异或得到。线性基可以高效解决与子集异或和相关的最值、排名、计数等问题。

  • 线性递推求解:对于递推关系 \(f_{n} = \sum_{i=1}^{k} c_{i} f_{n-i}\) ,当 \(n\) 极大时,需要高效算法。

  • 矩阵快速幂:将递推关系构造成 \(k \times k\) 的转移矩阵 \(M\) ,则 \(F_{n} = M^{n - k + 1} F_{k - 1}\) 。使用快速幂计算矩阵幂,复杂度为 \(O(k^3 \log n)\)

  • 多项式取模 (Cayley-Hamilton 定理): 根据 Cayley-Hamilton 定理, 转移矩阵满足其特征多项式. 求 \(f_{n}\) 等价于计算 \(x^{n} (\mod C(x))\) , 其中 \(C(x)\) 是特征多项式. 利用多项式快速幂和 NTT, 可将复杂度降至 \(O(k \log k \log n)\) .

  • BM (Berlekamp-Massey) 算法:一个强大的工具,可以在 \(O(N^2)\) 时间内,根据一个序列的前 \(2N\) 项,推断出该序列满足的最短线性递推关系。

34. 高级数论 (Advanced Number Theory)

深入计算数论的核心,处理模数非素或方程形式更复杂的情况。

  • exLucas(扩展卢卡斯定理):用于计算大组合数模任意整数 \(M\) 的值。其核心思想是先将 \(M\) 分解为质因数幂 \(p_i^{k_i}\) ,分别计算组合数在模 \(p_i^{k_i}\) 下的结果,最后用中国剩余定理 (CRT) 合并。
  • 离散对数 (Discrete Logarithm): 求解同余方程 \(a^x \equiv b (\bmod p)\) . 标准算法是 BSGS (Baby-Step Giant-Step), 通过分块思想, 在 \(O(\sqrt{p})\) 的时空复杂度内解决问题.
  • 阶与原根 (Order & Primitive Root): 整数 \(a \text{模} n\) 的阶是 \(a^k \equiv 1 \pmod{n}\) 的最小正整数 \(k\) . 若 \(a^k\) 的阶为 \(\phi(n)\) , 则称 \(g\) 为模 \(p\) 的一个原根. 原根的存在使得模 \(p\) 下的乘法群可以映射到指数上模 \(p - 1\) 的加法群, 是 NTT 和解决 N 次剩余等问题的基础.
  • N次剩余(N-th Residue):求解方程 \(x^{N}\equiv a\pmod{p}\) 。当模数存在原根时,可将问题转化为指数上的线性同余方程 \(Ny\equiv k\) (mod \(p - 1\) ,然后用扩展欧几里得算法求解。

35. 有向图专题 (Topics in Directed Graphs)

有向图的连通性比无向图复杂,衍生出许多重要概念和算法。

  • 强连通分量 (SCC): 有向图的极大子图, 其中任意两点互相可达。

  • Tarjan 算法:一次 DFS,利用 \(dfn\) 和 low 数组,通过栈在 \(O(V + E)\) 时间内找出所有 SCC。

  • Kosaraju 算法:两次 DFS,第一次在原图上确定拓扑序,第二次在反图上按拓扑序逆序进行 DFS,每次 DFS 访问到的节点构成一个 SCC。

  • 2-SAT (2-Satisfiability): 判定一类特殊布尔逻辑表达式是否有解。每个子句形如 \((A \lor B)\) , 可以转化为蕴含关系 \((\neg A \Rightarrow B)\)\((\neg B \Rightarrow A)\) 。为每个变量 \(x_{i}\) 及其否定 \(\neg x_{i}\) 建立一个点, 根据蕴含关系连边。若任意 \(x_{i}\)\(\neg x_{i}\) 在同一个 SCC 中, 则无解; 否则有解。

  • 支配树(Dominator Tree):在有向图 \(G\) 中,若从起点 \(r\) 到达节点 \(v\) 的每条路径都必须经过 \(u\) ,则称 \(u\) 支配 \(v\) 。所有节点与其唯一的直接支配点构成一棵以 \(r\) 为根的树,即支配树。它在分析控制流、寻找必经点等问题中非常有用。

36. 博弈论 (Game Theory)

主要关注公平组合游戏 (Impartial Games),即两名玩家交替行动,可选操作集合相同,有限步内结束且无平局。

  • 必胜态 (N-position) 与必败态 (P-position): N-position 是指当前玩家有必胜策略的状态; P-position 是指当前玩家无论如何操作, 对手都有必胜策略的状态。

  • Sprague-Grundy (SG) 定理:这是公平组合游戏的核心。它指出任何公平组合游戏都等价于一个特定大小的 Nim 游戏堆。这个等价的大小被称为该游戏状态的 SG 值或 Grundy 值。

  • 一个状态的 SG 值定义为其所有后继状态 SG 值的 mex (Minimum Excluded value): \(g(S) = \max \{g(S') \mid S'\)\(S\) 的后继状态 \(\}\)

  • 一个状态是 P-position(必败态)当且仅当其 SG 值为 0。

  • 游戏的和:一个由多个独立子游戏构成的组合游戏,其 SG 值等于所有子游戏 SG 值的异或和 (XOR sum)。这使得我们可以分解复杂游戏,分别计算 SG 值后合并。

37. 计算几何 (Computational Geometry)

用计算机高效、精确地解决几何问题,对代码细节和逻辑严谨性要求极高。

  • 基础运算:核心是向量运算,特别是点积(判断角度、投影)和叉积(判断方向、面积)。由于浮点数精度问题,比较大小时必须使用一个极小的 eps。

  • 凸包 (Convex Hull): 包围一个点集的最小凸多边形。

  • 求解算法:Graham扫描法(极角排序+栈)和Andrew算法(坐标排序+构造上下凸壳)都是 \(O(N\log N)\) 的经典算法。

  • 旋转卡壳 (Rotating Calipers): 一种基于凸包的 \(O(N)\) 技巧, 通过模拟一对平行卡尺旋转, 可以高效求解凸包的直径(最远点对)、宽度、最小外接矩形等问题。

  • 半平面交 (Half-Plane Intersection): 求解若干个半平面 (由直线 \(ax + by + c \geq 0\) 定义) 的交集, 结果是一个凸多边形。常用算法是 \(O(N \log N)\) 的排序+双端队列法。

  • Voronoi图与Delaunay三角剖分:两者互为对偶,是高阶计算几何的理论基石。Delaunay三角剖分满足“空圆性质”(任何三角形的外接圆不包含其他点),在网格生成、最近点对等问题中有重要应用。

38. 杂项算法与理论 (Miscellaneous Algorithms & Theories)

  • 欧拉回路/路径:在图中经过每条边恰好一次的回路/路径。其存在性有明确的充要条件,与图中顶点的度数(无向图)或出入度(有向图)有关。Hierholzer算法是经典的构造方法。
  • 竞赛图 (Tournament): 任意两点间都恰有一条有向边的特殊有向图。它具有许多优美性质, 如必定存在哈密顿路径, 强连通的竞赛图必有哈密顿回路。
  • Dilworth 定理与 DAG 最小路径覆盖:在偏序集(如 DAG)中,最小链覆盖的大小等于最大反链的大小。在 DAG 中,这可以用来求解最小路径覆盖问题,通过将其转化为二分图最大匹配解决:最小路径覆盖数 = 顶点数 - 最大匹配数。

39. 概率与期望 (Probability & Expectation)

  • 期望的线性性:随机变量之和的期望等于它们各自期望之和 \((E[X + Y] = E[X] + E[Y])\) ,无论变量是否独立。这是分解复杂期望问题的最有力工具。
  • 全期望公式: \(E[X] = \sum_{i} E[X|B_i]P(B_i)\) 。它将总期望分解为在不同条件下的期望的加权平均,是期望DP的理论基础。
  • 期望DP:用动态规划解决期望问题。状态通常定义为“从某状态到目标状态的期望值”,转移方程常根据全期望公式逆向建立。若状态转移有环,则需要高斯消元解线性方程组。
posted @ 2025-10-28 20:41  xihegudi  阅读(16)  评论(0)    收藏  举报