《算法分析与设计》学习笔记

作业占 \(20\%\),期中占 \(40\%\),项目(读 Paper,尝试实现或改进)占 \(40\%\)\(\newcommand{\de}{\delta}\newcommand{\al}{\mathcal}\newcommand{\fr}{\frac}\newcommand{\tio}{\tilde {\al O}}\newcommand{\a}{\alpha}\)

Lecture 1:近似最短路

内容:Dijkstra,APASP,近似距离查询。

很多内容 xtq 在 WC2024 上讲过。

最短路问题

\(\de(s, t)\) 表示 \(s\)\(t\) 的最短路,满足三角形不等式。

使用斐波那契堆优化的 Dijkstra 的复杂度为 \(\mathcal{O}(m + n \log n)\)

对于 APSP,Floyd-Warshall 的复杂度为 \(\mathcal{O}(n ^ 3)\)。更优?无向无权图 \(\al{O}(n ^ {\omega})\approx \al{O}(n ^ {2.38})\),有向无权图 \(\mathcal{O}(n ^ {\mu})\approx \mathcal{O}(n ^ {2.53})\)。猜想:当边权是 \(1\sim n\) 的整数时,不存在 \(\mathcal{O}(n ^ {3 - \de})\) 的 APSP 算法。

无向无权图 APASP

\(d(u, v)\) 为近似距离。

  • stretch \(k\) 近似满足 \(\de(u, v) \leq d(u, v)\leq k\de(u, v)\)
  • surplus \(t\) 近似满足 \(\de(u, v) \leq d(u, v)\leq \de(u, v) + t\)

\(\tio(n ^ {2.5})\) surplus \(2\)

准备工作

独立随机以 \(\fr 1 k\) 的概率选中一个点,得到期望大小为 \(\al {O}(\fr n k)\) 的点集 \(D\)。对于度为 \(d\) 的点,其有 \(1 - (1 - \fr 1 k) ^ d\) 的概率和 \(D\) 相邻。取 \(d = ck\log n\),则概率为 \(1 - \fr 1 {n ^ c}\)

定理

给定度数 \(d\),可以找到大小为 \(\tio(\fr n d)\) 的点集 \(D\),满足所有度不小于 \(d\) 的点大概率和 \(D\) 相邻。

\(V_1\) 是所有度不小于 \(\sqrt n\) 的点,\(D_1\) 是大小为 \(\tio (\sqrt n)\) 的点集,满足 \(V_1\) 的每个点和 \(D_1\) 相邻。

\(E_2\) 是所有 \((u, v)\) 满足 \(u\notin V_1\)\(v\notin V_1\),则 \(\deg(u)\)\(\deg(v)\) 不超过 \(\sqrt n\)\(|E_2| = \al O(n\sqrt n)\)

算法流程

  • 从每个 \(u\in D_1\) 跑 BFS 求出 \(\de(u, v)\),其中 \(u\in D_1\)\(v\in V\)。时间 \(\al O(|D_1||E|) = \tio (n ^ 2\sqrt n)\)
  • 从每个 \(u\in V\)\(E_2\cup \de(D_1\times V)\) 上跑 Dijkstra。时间 \(\tio(n(|E_2| + n|D_1|)) = \tio(n ^ 2\sqrt n)\)

算法的时间复杂度 \(\tio(n ^ 2\sqrt n)\)

算法分析

  • \(u\in D_1\)\(v\in D_1\),则 \(\de(u, v)\) 在第一步求出。
  • \(u, v\) 最短路径上所有点的度小于 \(\sqrt n\),则每条边在 \(E_2\) 上,\(\de(u, v)\) 在第二步求出。
  • 若存在度不小于 \(\sqrt n\) 的点 \(w\),则 \(w\)\(w'\in D_1\) 相邻,\(d(u, v)\leq \de(u, w') + \de(w', v) \leq \de(u, v) + 2\)

通过 \(D_1\) 把路径上度数较大的点移动到较小的集合内。

\(\tio(n ^ {7 / 3})\) surplus \(2\)

这个算法还有可以利用的性质:最短路径要么只有 \(\de(D_1\times V)\),要么只有 \(E_2\),两部分独立。\(\delta(D_1\times V)\) 的部分已经是近似长度,但 \(E_2\) 的部分求出了精确值,这里还有优化空间。

改改阈值会发生什么?

\(V_1\) 是所有度不小于 \(n ^ {2 / 3}\) 的点,则 \(|D_1| = \tio(n ^ {1 / 3})\)。算法的第一步时间 \(\tio (n ^ {7 / 3})\),但 \(|E_2| = \al O(n ^ {5 / 3})\),无法 Dijkstra。这一步解决了 \(u\)\(v\) 度不小于 \(n ^ {2 / 3}\) 或最短路径上有度不小于 \(n ^ {2 / 3}\) 的点的情况,且 接下来只需考虑边集为 \(E_2\) 的情况,设新图为 \(G'\)

再分一层 \(V_2\) 表示所有度不小于 \(n ^ {1 / 3}\) 的点,则 \(|D_2| = \tio(n ^ {2 / 3})\)。设 \(V_2\) 对应 \(E_3\),则 \(|E_3| = \al{O}(n ^ {4 / 3})\)。这一步解决了最短路径上所有点度小于 \(n ^ {1 / 3}\) 的情况(实际上,没有两个相邻的点度均不小于 \(n ^ {1 / 3}\) 的情况)。

对于路径上有度在 \(n ^ {1 / 3}\)\(n ^ {2 / 3}\) 之间的点的情况,设 \(w\) 是最后一个这样的点,则 \(w\)\(v\) 都在 \(E_3\) 上,存在 \(w'\in D_2\)\(w\) 相邻。我们要求 \(u\)\(w'\) 之间在 \(G'\) 上的最短路 \(\de'(u, w')\)。这可以通过从 \(D_2\) 的每个点 BFS 得到,时间 \(\al{O}(|D_2||E_2|) = \tio(n ^ {7 / 3})\)

但这样还有一个问题,就是 \(\de'(D_2\times V)\) 的大小是 \(\tio(n ^ {5 / 3})\),不能直接丢进 Dijkstra。设 \(E ^ *\) 为每个 \(w\in V_2\) 和一个邻居 \(w'\in D_2\) 之间的边集,则 \(|E ^ *| = \al O(n)\)。将 \(E ^ *\) 丢入最终的 Dijkstra,这样就只要求最终能考虑到 \(\de'(\{u\}\times D_2)\)。所以从 \(u\) 跑 Dijkstra 的时候只要把 \(\de'(\{u\}\times D_2)\) 丢进去就行。

算法流程

  • 从每个 \(u\in D_1\) 跑 BFS 求出 \(\de(u, v)\),其中 \(u\in D_1\)\(v\in V\)。时间 \(\al O(|D_1||E|) = \tio (n ^ {7 / 3})\)
  • 从每个 \(u\in D_2\)\(E_2\) 上跑 BFS 求出 \(\de'(u, v)\),其中 \(u\in D_2\)\(v\in V\)。时间 \(\al O(|D_2||E_2|) = \tio (n ^ {7 / 3})\)
  • 从每个 \(u\in V\)\(E_3\cup \de(D_1\times V)\cup \de'(\{u\}\times D_2)\cup E ^ *\) 上跑 Dijkstra。时间 \(\tio(n(|E_3| + n|D_1| + |D_2| + n)) = \tio(n ^ {7 / 3})\)

通过 \(D_1\) 把路径上度数较大的点移动到较小的集合内,并删去 \(E_2\) 以外的所有边。

通过 \(D_2\) 把路径上度数适中的点移动到较小的集合内,只需保证最后一个度数适中的点被考虑到,并以此减少 Dijkstra 算法的边数。

算法分析

  • \(u\in D_1\)\(v\in D_1\),则 \(\de(u, v)\) 在第一步求出。
  • \(u, v\) 最短路径上所有点的度小于 \(n ^ {1 / 3}\),则每条边在 \(E_3\) 上,\(\de(u, v)\) 在第三步求出。
  • \(w\) 是路径上最后一个度在 \(n ^ {1 / 3}\)\(n ^ {2 / 3}\) 之间的点,\(w'\in D_2\)\((w, w')\in E ^ *\)。路径 \(\de'(u, w') + \de(w', w) + \de(w, v)\leq \de(u, v) + 2\) 且三部分分别属于 \(\de'(\{u\}\times D_2)\)\(E ^ *\)\(E_3\)

扩展

分更多层会出现更多中转点,可以做到 \(\tio(kn ^ {2 + \fr 1 {3k - 4}})\) 的无向无权图 surplus \(2(k - 1)\) APASP。当 \(k = \al O(\log n)\) 时,存在 \(\tio (n ^ 2)\) APASP。

无向无权图 surplus \(2\) APASP 在 2022 年被邓老师等人改进到了 \(\tio(n ^ {2.29})\)

\(\tio(n ^ {2.29})\) surplus \(2\)

普通 \(\min +\) 矩阵乘法(距离乘法)暂无 \(\al O(n ^ {3 - \de})\) 的算法,但 BD 矩阵(只需一个方向的 BD)存在 \(\tio(n ^ {2.687})\) 的距离乘法。

大概思想是观察到 \(\de(D_1\times V)\) 的部分只有两条边,相当于 \(\de(V\times D_1)\)\(\de(D_1\times V)\) 的距离乘法。用欧拉序将矩阵化为 BD,把这部分复杂度变优,就可以更改阈值做到更优复杂度了。

没讲怎么改进 \(\de(D_1\times V)\) 的求解。

分成 \(\al O(\log n)\) 层可以进一步做到 \(\al O(n ^ {2.2593})\)

\(\tio(n ^ 2)\ (2, 1)\) 近似

\(\de(u, v)\leq d(u, v)\leq 2\de(u, v) + 1\)

算法流程

对每个 \(0\leq i\leq \log_2 n\),设 \(V_i\) 为所有度不小于 \(\fr {n} {2 ^ i}\) 的点,得到大小为 \(\tio(2 ^ i)\) 的覆盖集 \(D_i\) 和对应的 \(E ^ *\)。设 \(E_i\) 为所有 \(u\notin V_{i - 1}\)\(v\notin V_{i - 1}\) 的边。

  • \(D_i\) 的每个点在 \(G_i = (V, E_i\cup E ^ *)\) 上跑 BFS 求出 \(\de_i(D_i\times V)\)。时间 \(\al O(|D_i|(|E_i| + n)) = \tio(n ^ 2)\)

\(c_i(u)\)\(G_i\) 上距离 \(u\) 最近的 \(D_i\) 的点。

  • \(\de_i(u, c_i(u)) + \de_i(c_i(u), v)\)\(\de_i(u, c_i(v)) + \de_i(c_i(v), v)\) 更新 \(d(u, v)\)

算法分析

考虑 \(u, v\) 最短路上度数最大的点 \(x\)。因为 \(V_0 = \varnothing\),所以存在 \(i\) 使得 \(x\in V_i\)\(x\notin V_{i - 1}\)。此外,因为 \(x\) 的度数最大,所以路径上所有边都在 \(G_i\) 上。设 \(w\in D_i\)\(x\) 相邻且 \((w, x)\in E ^ *\),则 \(u\rightsquigarrow x\to w\)\(w\to x\rightsquigarrow v\) 都在 \(G_i\) 上。

\(u' = c_i(u)\)\(v' = c_i(v)\),于是 \(\de_i(u, u') \leq \de(u, x) + 1\)\(\de(v, v')\leq \de(v, x) + 1\),不妨设 \(\de_i(u, u') \leq \lfloor\fr {\de(u, v)} {2}\rfloor + 1\),则

\[\de_i(u, u') + \de_i(u', v) \leq 2\de_i(u, u') + \de_i(u, v) \leq 2\de(u, v) + 2 \]

  • 当路径长度是偶数时,如果 \(x\) 不在中点,不妨设靠近 \(u\) 一侧,那么 \(\de_i(u, u') + \de_i(u', v)\leq 2\de(u, v)\)。如果 \(x\) 在中点,那么考虑度数次大的点 \(x'\) 以及对应的 \(j\),根据 \(E_j\) 的定义可知路径上所有边在 \(E_j\) 上,此时 \(x'\) 一定不在中点。
  • 当路径长度是奇数时,\(\de_i(u, u') + \de_i(u', v) \leq 2\de(u, v) + 1\)。当 \(x\)\(x'\) 分别是最中间一条边的两端时,不等式可能取等。

为什么无向无权

对于有向图,以上算法显然行不通。

对于带权图,和一个点相邻不一定距离这个点近。可以考虑乘法近似。

无向带权图 APASP

截断 Dijkstra

考虑求出距离一个点最近的 \(b\) 个点,普通 Dijkstra 需要 \(\tio(n\cdot b)\) 的时间。截断 Dijkstra 在普通算法上加入改进:只考虑一个点权值最小的 \(b\) 条出边。

时间 \(\tio(b ^ 2)\)

\(\tio(n ^ {7 / 3})\) stretch \(3\)

类似的思想。

\(B(u)\) 是距离 \(u\) 最近的 \(n ^ {2 / 3}\) 个点,\(D\) 是随机选出的大小为 \(\tio(n ^ {1 / 3})\) 的点集,满足所有 \(B(u)\)\(D\) 有交。

这样,从 \(D\) 出发跑 Dijkstra,如果 \(u\in D\)\(v\in D\)\(v\in B(u)\),得到最短路。否则存在 \(w\in B(u)\cap D\)。因为 \(v\notin B(u)\),所以 \(\de(u, w)\leq \de(u, v)\)。同时 \(\de(w, v)\leq \de(w, u) + \de(u, v)\leq 2\de(u, v)\),所以 \(\de(u, w) + \de(w, v) \leq 3\de(u, v)\)

显然复杂度为 \(\tio(n ^ {7 / 3})\)

Cohen-Zwick 在 1997 年做到了 \(\tio(n ^ {3 / 2}m ^ {1 / 2})\) stretch \(2\)\(\tio(n ^ {7 / 3})\) stretch \(\fr 7 3\)\(\tio(n ^ 2)\) stretch \(3\)(可用 \(\tio(n ^ 2)\ (2, 1)\) 近似解决)。

近似最短路查询

以上讨论给出了使用 \(\al O(n ^ {5 / 3})\) 空间,\(\tio(n ^ {7 / 3})\) 预处理的 \(\al O(1)\) stretch \(3\) 近似最短路查询。

可以用 \(\al{O}(kn ^ {1 + \fr 1 k})\) 的空间做 \(\al O(k)\) stretch \(2k - 1\) 近似最短路查询 [Thorup, Zwick, 2001]。

Lecture 2:斐波那契堆

内容:二叉堆、二项树、斐波那契堆。

以下所有内容考虑小根堆:每个点的权值不大于其儿子权值。

二叉堆

堆的结构是一棵完全二叉树。根是最小值,树高 \(\log_2 n\)

如何实现?用指针维护父亲和儿子关系,或左儿子两倍右儿子两倍加一。

查询最小值

\(\al O(1)\)

插入

\(x\) 插入下一个叶子的位置,向上冒泡。\(\al O(\log n)\)

减法

将元素的值减去 \(k\),向上冒泡。\(\al O(\log n)\)

删除最小值

将根和最右侧叶子交换,向下冒泡(和较大的儿子交换)。\(\al O(\log n)\)

合并

暴力合并。\(\Omega(n)\)

二项堆

二项树

\(B_0\) 是一个点,\(B_k\)\(B_{k - 1}\) 在根底下接一棵 \(B_{k - 1}\)

性质:

  • \(|B_k| = 2 ^ k\)
  • \(B_k\) 深度为 \(k\)
  • \(B_k\) 的第 \(i\) 层有 \(\binom k i\) 个点。

定义一个 结点的阶 是其儿子个数,一棵 二项树的阶 是其根的阶。

原则:只合并同阶二项树

二项堆

二项堆维护的过程借助了二进制分组的思想保证复杂度:一旦有两棵同阶二项树,则合并。因此,若二项堆的大小为 \(n\),则该二项堆恰含所有 \(n\)\(2\) 进制下为 \(1\) 的位对应的二项树。

一个二项堆最多有 \(\lceil \log_2 n\rceil\) 棵二项树。

合并

模拟二进制加法。\(\al O(\log n)\)

删除最小值

找到最小值,删去。对应二项树分裂成若干二项树,与原有二项树合并。\(\al O (\log n)\)

减法

向上冒泡。\(\al O(\log n)\)

删除

减到 \(-\infty\) 再删除最小值。\(\al O(\log n)\)

插入

二进制模拟加 \(1\)\(\al O(\log n)\),均摊 \(\al O(1)\)

查询最小值

\(\al O(\log n)\)

斐波那契堆

斐波那契堆只有删除(最小值)的复杂度为 \(\al O (\log n)\)。其余操作均为均摊 \(\al O(1)\)

从空斐波那契堆开始,任何 \(a_1\) 次插入、\(a_2\) 次删除和 \(a_3\) 次减法的操作序列需要 \(\al O(a_1 + a_2\log n + a_3)\) 时间。

做到 \(\al O(1)\) 插入、删除和减法是不可能的,因为基于比较的排序有下界 \(\Omega(n\log n)\)。因此,斐波那契堆相当优秀。

其基本想法和二项堆类似,但对结构的要求更宽松。二项堆在每次操作后立刻重构,斐波那契堆采用 懒重构 的思想,直到删除最小值之后再重构。

维护:

  • 一些堆。
  • 根的列表和最小值指针。
  • 结点的标记:表示该结点已经失去了一个儿子。如果再失去一个,则以它为根的子树会被剪下来。

\(R(x)\)\(R(H)\) 表示阶,\(T(H)\) 表示树的个数,\(M(H)\) 表示被标记的结点数量。定义势能 \(\Phi(H) = 2T(H) + M(H)\)

定义均摊时间为实际用时加上势能的变化量,则总用时不超过总均摊时间之,因为势能非负且初始为 \(0\)。势能可能需要乘以一些常数。

插入

将结点插入根的列表,更新最小值指针。

时间 \(\al O(1)\),势能 \(+1\),均摊 \(\al O(1)\)

合并两棵树

将较大的根连到较小的根下面。

删除最小值

把根的所有子树全部融到根列表,重构使得没有根的阶相同。

时间 \(\al O(R(H) + T(H))\),势能 \(\al O(R(H')) - T(H)\),均摊(给势能乘以一定常数)\(\al O(R(H))\)

在只有前述操作时,\(R(H) = \al O(\log n)\)

减法

如果堆性质没有被破坏,则直接减。

否则把以 \(x\) 为根的树剪下,加入根列表,标记其父亲(除非其父亲为根)。

如果不特殊处理,这一步会扭曲树的形态,导致 \(R(H)\) 过大。为了保证树的平整,如果一个点已经被标记两次,则取消标记,并将以该点为根的子树剪下,标记其父亲(除非其父亲为根)。这一步可能递归。

设剪下子树的次数为 \(c\),则时间 \(\al O(c)\),势能 \(\al O(1) - c\),均摊 \(\al O(1)\)

分析

\(x\) 的儿子按照连边顺序记为 \(y_1, \cdots, y_k\)。当 \(y_i\) 连向 \(x\) 时,\(R(y_i)\) 等于当时的 \(R(x) \geq i - 1\)。而接下来 \(y_i\) 最多失去一个儿子。于是 \(R(y_i)\geq i - 2\)

\(F_k\)\(k\) 阶树的最小大小,则 \(F_1 = 1\)\(F_1 = 2\)\(F_k = F_{k - 2} + \cdots + F_0 + 1 = F_{k - 2} + F_{k - 1}\),这是斐波那契数列。于是 \(R(H)\leq \log_{\phi} n\)

合并

用双向列表维护根列表。

时间 \(\al O (1)\),势能 \(0\),均摊 \(\al O(1)\)

应用

\(\al O(m + n\log n)\) 最小生成树

在 Dijkstra 或 Prim 的过程中使用斐波那契堆,\(n\) 次插入,\(n\) 次删除和 \(m\) 次减法,时间 \(\al{O}(m + n\log n)\)

\(\al O(m + n\log ^ *n)\) 最小生成树

Fredman & Tarjan [1987] 通过限制堆的大小做到了更优的复杂度:考虑结合 Boruvka 和 Prim,从某个点开始,如果堆里有 \(k\) 个元素或碰到了当前生成森林,则停止本次 Prim,从另一个未被访问的点开始。一轮过后,将森林里的所有树缩点,则每个点至少 \(k\) 条出边,结点数量不超过 \(\fr {2m} k\)

\(k = 2 ^ {2m / n}\),则一轮复杂度 \(\al O(m + n\log k) = \al O(m)\)。新的 \(k' = 2 ^ {2m / n'} = 2 ^ {2 ^ {2m / n}}\)。可知总轮数为 \(\beta(m, n) = \min\{i: \log ^ {(i)} n \leq \fr m n\}\),其中 \(\log ^ {(i)} n\) 表示取 \(i\)\(\log\)

因此,当 \(m = \al O(n)\) 时,时间为 \(\al O(m \log ^ * n)\)。一旦存在常数 \(k\) 使得 \(m = n\log ^ {(k)} n\),则时间为 \(\al {O}(m)\)

目前学术界的 MST

随机算法 \(\al O(m)\) [Karger, Klein, Tarjan 1995]。

确定算法 \(\al O(m\alpha(m, n))\) [Chazalle 2000]。

最优算法(复杂度仍为 open problem) [Pettie, Ramachandran 2002]。

判定 MST \(\al O(m)\) [Dixon, Rauch, Tarjan 1992]。

Tarjan 老爷子是真牛啊。

Lecture 3:并查集

内容:并查集的复杂度分析。

问题描述

初始 \(n\) 个集合 \(\{1\}, \cdots, \{n\}\),支持合并两个集合,以及查找某个元素在哪个集合。

并查集是维护不交集合的数据结构,每个集合用一个代表元表示,支持:

  • Makeset(x):新建包含元素 \(x\) 的集合。
  • Find(x):查询 \(x\) 所在集合的代表元。
  • Union(x, y):若 \(x, y\) 处于不同集合,则合并它们所在的集合,删去原有的两个集合。

以下设 \(n\) 为元素数量,\(m\) 为操作次数。

链表

用链表维护,每个元素指向下一个元素和代表元。

makeset \(\al O(1)\),find \(\al O(1)\),union \(\al O(n)\)

如果不维护代表元指针,则 find \(\al O(n)\),union \(\al O(1)\)

启发式合并

将较短的链表添加在较长的链表后,时间 \(\al O(m + n\log n)\):元素代表元更新,其所在链表长度至少翻倍。

Up-tree

每个点指向其父节点,没有父节点的点为对应集合代表元。

合并可能导致树的深度达到 \(\al O(n)\),使得 find \(\al O(n)\)

按秩合并

将深度较小的树合并到深度较大的树:深度为 \(k\) 的树至少有 \(2 ^ k\) 个点。

将大小较小的树合并到大小较大的树:每个点被合并时,其所在集合大小至少翻倍。

时间复杂度 \(\mathcal{O}(m\log n)\)

路径压缩

查询时将路径上的每个点都指向代表元。

复杂度分析

重点是分析路径压缩按秩合并的并查集的复杂度。

\(r(x)\) 表示 \(x\) 的秩,初始为 \(0\)\(r(x)\) 增加 \(1\) 当且仅当 \(y\) 合并到 \(x\) 上且 \(r(x) = r(y)\)。可知阶为 \(r\) 的点数为 \(\al O(\fr {n} {2 ^ r})\)

因为 union 等价于两个 find 和一个 link,且 makeset 和 link 显然为 \(\al O(1)\),所以只考虑 \(m\) 个 find 的均摊时间。

\(\al O((m + n)\log \log n)\)

考虑 \(r(p(x)) - r(x)\)。每次查询,最多 \(\al O(\log \log n)\) 个点的该值没有翻倍,因为每个这样的点都会让 \(r(rep(x)) - r(x)\) 翻倍。

对于那些翻倍的点,最多发生 \(\al O(\log r(x)) = \al O(\log \log n)\) 次。

时间为 \(\al O((m + n)\log\log n)\)

\(\al O(m + n\log ^ *n)\)

\(T(m, n, r)\) 表示 \(n\) 个点 \(m\) 次操作且秩不超过 \(r\) 的最大指针赋值次数,显然 \(T(m, n, r) \leq nr\)

\(s\) 是秩的阈值。

考虑一次 find 从 \(x\) 开始,且根为 \(y\)

  • \(r(y) \leq s\),则总赋值次数不超过 \(T(m, n, s)\)
  • \(r(x) > s\),则总赋值次数不超过 \(\fr {nr} {2 ^ s}\)
  • \(r(x) \leq s < r(y)\)
    • \(r(p(x))\leq s\),考虑到每个点的父亲的秩最多一次从不超过 \(s\) 变成大于 \(s\),总赋值次数不超过 \(n\)
    • \(r(p(x)) > s\),每次操作最多一个这样的点,总赋值次数不超过 \(m_+\),其中 \(m_+\) 表示涉及到阶大于 \(s\) 的点的 find 次数,\(m_- = m - m_+\)

于是

\[\begin{aligned} & \; T(m, n, r)\leq T(m_-, n, s) + \fr {nr} {2 ^ s} + m_+ + n \\ = & \; T(m, n, r) - m \leq (T(m_-, n, s) - m_-) + \fr {nr} {2 ^ s} + n \\ = & \; T'(m, n, r) \leq T'(m_-, n, s) + \fr {nr} {2 ^ s} + n \\ \end{aligned} \]

\(s = \log r\),则

\[\begin{aligned} & T'(m, n, r)\leq T'(m_-, n, \log r) + 2n \\ \implies & T'(m, n, r)\leq 2n \log ^ * r \\ \implies & T(m, n, r) \leq m + 2n\log ^ * r \end{aligned} \]

\(\al O((m + n)\a(n))\)

Ackermann's function

定义

\[A_m(n) = \begin{cases} n + 1, & m = 0; \\ A_{m - 1} ^ {(n + 1)}(1), & m \geq 1. \end{cases} \]

分析

对于

\[T(m, n, r)\leq T(m_-, n, s) + \fr {nr} {2 ^ s} + m_+ + n \]

第二项不超过 \(T(m_+, \fr n {2 ^ s}, r) \leq m_+ + \fr {2n\log ^ * r} {2 ^ s}\),于是

\[T(m, n, r)\leq T(m_-, n, s) + 2m_+ + \fr {2n \log ^ * r}{2 ^ s} + n \]

\(s = \log ^ * r\),则

\[T(m, n, r)\leq T(m_-, n, \log ^ * r) + 2m_+ + 3n \]

\(T''(m, n, r) = T(m, n, r) - 2m\),则

\[T''(m, n, r) \leq T''(m_-, n, \log ^ * r) + 3n \]

\[T(m, n, r)\leq 2m + 3n \log ^ {**}r \]

不断重复上述过程,得

\[T(m, n, r) \leq c(m + n) \log ^ {* ^ c} r \]

\(\log ^ {* ^ c} r\) 表示取多少次 \(\log ^ {* ^ {c - 1}}\) 可以让 \(r\) 变成常数,而

\[\a(n)\approx \min \{c\geq 1\mid \log ^ {* ^ c} n\leq 3\} \]

因此时间为 \(\al O((m + n)\a(n))\) [Tarjan 1975]。

是否存在更优美的复杂度分析?Tarjan 同时证明了这个算法的时间下界 \(\Omega(m\a (n))\)

复杂度下界分析

略。

对任何并查集,有 \(\Omega(\a(n))\) word-operations 的下界 [Fredman, Saks 1989]。

Lecture 4:动态树

Splay

\(\mu(x) = \log |S(x)|\)\(\Phi(S) = \sum_{x\in S} \mu(x)\)

Splay 引理:每个 splay(x, S) 操作需要 \(3(\mu(S) - \mu(x)) + 1\) 的均摊时间。

  • 每个 Zig 不超过 \(3(\mu(S) - \mu(x)) + 1\)
  • 每个 Zig-Zig 和 Zig-Zag 不超过 \(3(\mu'(x) - \mu(x))\)

证明的 nasty-case 是 \(\mu(x) = \mu'(x)\),此时可以证明 \(\mu'(x) + \mu'(y) + \mu'(z) < \mu(x) + \mu(y) + \mu(z)\)

动态最优性猜想:\(C_{\rm{SPLAY}}(s) = \al O(n + C_{\rm{OPT}}(s))\)

LCT

对 link 的要求:\(v\)\(w\) 不在同一棵树,且 \(v\) 是根。

对 delete 的要求:删除树边。

每个结点有 0/1 个偏好的孩子。用 Splay 维护偏好链,每个 Splay 指向父亲。

access 打通 \(x\) 到根的路径,模拟即可。

ETT

用 Splay 维护欧拉序。

换根:考虑 \(r\) 的任意一次出现,将欧拉序分为 \(A\)\(B\),其中 \(A\)\(r\) 之前的部分。删除 \(A\) 的第一个点,加入 \(r\),然后将 \(A\) 放在 \(B\) 之后。

换根之后容易做 link 和 cut。

LCT 做链问题比较优秀,ETT 做子树问题比较优秀。

Lecture 5:动态连通性

完全动态连通性

加边删边查询连通性。想法:维护生成森林。

加边容易,但删树边时需要找到重新连接两部分的边。

每条边有只会增加的等级 \(l_e\),设 \(E_i\) 为等级不小于 \(i\) 的边,则 \(E_0\supseteq E_1 \supseteq \cdots \supseteq E_{l_\max}\)

维护 \(E_i\) 的生成森林 \(F_i\),满足

  • \((u, v)\in E_i\) 是非树边,则 \(u, v\)\(F_i\) 上连通(生成森林极大)。
  • \((u, v)\in E_i\) 是树边,则 \((u, v)\)\(F_j(j < i)\) 上也是树边。
  • \(F_i\) 的每棵树的大小不超过 \(\fr {n} {2 ^ i}\)

可知若一条边在 \(E_i\) 上是非树边,那么它在所有它在的 \(E_j\) 上是非树边。

加入 \((u, v)\) 时,将其加入 \(E_0\)。若 \(u, v\) 连通,则为非树边,否则为树边。

删除 \((u, v)\) 时,在所有 \(E_{0\sim l}\) 上删去 \((u, v)\)。若是树边,则删去 \((u, v)\) 会将 \(F_l\) 对应的树分裂成 \(T(u)\)\(T(v)\) 两部分。其中一部分的大小小于原树的一半,不妨设为 \(T(u)\),则将 \(T(u)\) 加入 \(F_{l + 1}\)。然后检查所有和 \(T(u)\) 相连的非树边,若不连接 \(T(u)\)\(T(v)\),则一定在 \(T(u)\) 内部,移到下一个等级,否则直接连起来。如果等级 \(l\) 没有,那么检查等级 \(l - 1\),以此类推。

动态子图连通性

忽略所有 \(\log\) 因子。

每个点有开关状态,支持修改一个点的状态,以及查询两个点能否通过开着的点连通。

维护两个集合 \(P, Q\),初始所有点都在 \(P\)\(Q\) 为空。\(P\) 只会删点。打开一个点时将其加入 \(Q\)。每 \(m ^ {2 / 3}\) 次修改重构一次,所以 \(Q\) 的大小不超过 \(m ^ {2 / 3}\)

用删边维护的方法维护 \(P\) 的连通性。称总度数大于 \(m ^ {1 / 3}\) 的连通块为大连通块,数量不超过 \(m ^ {2 / 3}\),否则为小连通块。

维护新图 \(G ^ *\),结点包含 \(Q\)\(H\),其中 \(H\) 的每个元素表示 \(P\) 的一个大连通块,以及原图连接 \(Q\) 以及 \(Q\)\(H\) 之间的边。

如果 \(Q\) 的结点可以通过小连通块连通呢?若 \(u, v\in G\) 通过小连通块连通,则将 \((u, v)\) 加入 \(\Gamma\),其大小为 \(m ^ {4 / 3}\)。将 \(\Gamma\) 加入 \(G ^ *\),则 \(u, v\in Q\) 连通当且仅当它们在 \(G ^ *\) 上连通。

对于查询:

  • 对于 \(Q\) 里的点,\(\log n\) 查询在哪个连通块。
  • 对于大连通块的点,查询在 \(G ^ *\) 上表示这个大连通块的点,再查询在哪个连通块。
  • 对于小连通块的点,找一个 \(Q\) 里的点与其相邻。若不存在则孤立。\(m ^ {1 / 3}\)

对于预处理:

  • \(G ^ *\)\(\Gamma\) 都需要 \(m ^ {4 / 3}\) 的时间。
  • \(m ^ {2 / 3}\) 次修改重构一次,所以均摊到每个修改 \(m ^ {2 / 3}\)

对于修改:

  • 当加入或删除 \(Q\) 的点时,更新 \(G ^ *\) 的每个点和这个点的连接情况。\(m ^ {2 / 3}\)
  • 删除小连通块的点时,重新计算相关的 \(\Gamma\)\((m ^ {1 / 3}) ^ 2 = m ^ {2 / 3}\)
  • 删除大连通块的点时,按度数之和从大到小记分裂成的连通块为 \(R_1, \cdots, R_k\)
    • 删掉 \(P\) 的边的总数为 \(m\),所以每个修改 \(m ^ {1 / 3}\)
    • 如果 \(R_i(i\geq 2)\) 是大连通块,则更新 \(G ^ *\) 需要 \(\deg(R_i)\) 的时间。\(\deg (R_i)\) 减半,最多 \(\log m\) 轮。\(m ^ {1 / 3}\)
    • 如果 \(R_i\) 从大连通块变成小连通块,则更新 \(\Gamma\)。因为只会变一次,所以 \(m ^ {2 / 3}\)

Lecture 6:最大流

FF

每次找一个增广路。若所有容量是 \([0, U]\) 的整数,则算法在 \(|f ^ *|\leq nU\) 轮结束。当 \(U = 1\) 时,时间 \(nm\)

整性定理:若网络所有容量都是整数,则存在所有边的流量都是整数的最大流。

EK

选增广路的时候不要随便选。选最大瓶颈路,足够大容量的路,或者最短路。

Capacity Scaling

\(\Delta\) 为不小于 \(U\) 的最大的二的幂。在边权 \(\geq \Delta\) 的图上找增广路,最多找 \(\al O(m\Delta)\) 次,然后将 \(\Delta\) 除以 \(2\)\(m ^ 2 \log U\)

需要强多项式算法。

最短路

指边数最少的路径。

考虑最短路分层图,每次删去至少一条边,反边不会出现在分层图上。最多增广 \(m\) 次后得到分层图上的阻塞流,最短路增加。\(m ^ 2 n\)

Dinic

不要每次都在分层图上重新找,一次找完即可。\(n ^ 2 m\)

用 LCT 维护分层图的生成树,\(nm\log n\)

单位容量最大流

任意图

\(\al O(m)\) 的时间找到阻塞流,因为每条路径上的所有边被立刻删去。

\(n ^ {2 / 3}\) 轮之后存在相邻两层 \(n_i + n_{i + 1} \leq 2n ^ {1 / 3}\),剩余流量不超过 \(n ^ {2 / 3}\)

\(m ^ {1 / 2}\) 轮之后存在相邻两层 \(m_i\leq m ^ {1 / 2}\),剩余流量不超过 \(m ^ {1 / 2}\)

\(m \min(n ^ {2 / 3}, m ^ {1 / 2})\)

无向图

在残量网络上,只有有流量的边有方向。在单位容量无向图上,任何无环流可以被分解为路径。

如果残量网络的最大流为 \(v\),则最短路不超过 \(\fr {n} {\sqrt v}\),因为每相邻两层至少有 \(2\sqrt v\) 个点。

因为最大流不超过 \(n\),所以无环流最多有 \(\fr n {\sqrt n} + \fr n {\sqrt {n - 1}} + \cdots + n = n ^ {3 / 2}\) 条边。因此寻找增广路时最多有 \(n ^ {3 / 2}\) 条有向边。对于无向边,用完全动态连通性维护,BFS 时只需考虑 \(n ^ {3 / 2}\) 条边。复杂度 \(\tio (n ^ {5 / 2})\)

Lecture 7:最小割和 k 连通性

全局最小割

有平凡的 \(n\cdot \operatorname{flow}\) 做法,略。

Karger' Algorithm

随机选一条边收缩,只剩两个点时得到割,但不一定是最小割 \(c\)。当且仅当割的每条边都没有被收缩时正确。

因为每个点的度数不小于 \(c\),所以边数不小于 \(\fr {nc} 2\)。第 \(i\) 次选中的概率为 \(\fr {c} {(n - i)c / 2} = \fr 2 {n - i}\),所以正确率为

\[\fr {n - 2} n \fr {n - 3} {n - 1} \cdots \fr 2 4 \fr 1 3 = \fr 1 {n ^ 2} \]

于是复杂度 \(n ^ 4\)。同时证明了全局最小割的数量 \(\al O(n ^ 2)\)

考虑在还剩 \(\fr n {\sqrt 2}\) 个点时停下,此时正确率为 \(\fr 1 2\)。在剩下的小图上跑两次。时间

\[T(n) = 2T\left(\fr n {\sqrt 2}\right) + \al O(n ^ 2) = \al O(n ^ 2\log n) \]

正确率

\[P(n) \geq 1 - \left(1 - \fr 1 2 P\left(\fr n {\sqrt 2}\right)\right) ^ 2 \]

\(P(n) \geq P\left(\fr n {\sqrt 2}\right) - \fr 1 4P\left(\fr n {\sqrt 2}\right) ^ 2\)。设 \(q(k) = \fr 4 {P(k)} - 1\),则 \(q(n)\leq q\left(\fr n {\sqrt 2}\right) + 1 + \fr 1 {q\left(\fr n {\sqrt 2}\right)}\)

\(Q(n)\)\(q(n)\) 的上界,则 \(Q(n)\geq \log_{\sqrt 2} n + K\)。展开递归式,由调和级数得

\[Q(n) < \log_{\sqrt 2}n + \fr 1{Q\left(\fr n {\sqrt 2}\right)} + \fr 1{Q\left(\fr n {2}\right)} + \cdots + \fr 1 {Q(6)} + K' = \al O(\log n) \]

不能用在 s-t 最小割,因为度数下界没有保证。

k 连通性

最简单的一集。

k 边连通:删去少于 \(k\) 条边,仍连通。

k 点连通:删去少于 \(k\) 个点,仍连通。要求 \(G\) 有多于 \(k\) 个点。

k 点连通,则 k 边连通。

双连通分量的判定:Tarjan。

耳分解不在考纲内,略。

Lecture 8:原始对偶松弛算法

集合覆盖

一个元素的频率为它所在的集合数量,\(f\) 为最大频率。点覆盖是 \(f = 2\) 的集合覆盖。

集合覆盖写成整数线性规划 IP:对每个 \(S_i\),有变量 \(x_i\in \{0, 1\}\) 表示是否选择。最小化 \(\sum_{i = 1} ^ k c(S_i)x_i\),要求 \(\sum_{i : e\in S_i} x_i \geq 1,\ \forall e\in U\)\(x_i\in \{0, 1\}\)

松弛成线性规划 LP:\(0\leq x\leq 1\)。设 LP 的最优解为 \(x ^ *\),将 \(S_j\) 加入 \(I\) 当且仅当 \(x_j ^ *\geq \fr 1 f\),则 \(I\) 是集合覆盖且是 \(f\)-近似。

\[\sum_{j\in I} c(S_j)\leq \sum_{j\in I} c(S_j)x_j ^ *f\leq f\sum_{j} c(S_j)x_j ^ *\leq f\cdot \operatorname{OPT} \]

LP 的最优解显然不劣于 IP 的最优解。

线性规划

最小化 \(c ^ T x\),要求 \(Ax\geq b\)\(x\geq 0\)

LP 算法的复杂度,其中 \(L\) 是输入的比特数:

  • Simplex:指数,但很快。
  • Ellipsoid:\(\tio (n ^ 6L ^ 2)\)
  • Interior Point:\(\tio(n ^ {3.5}L ^ 2)\)

Open Problem:是否存在强多项式的算法。

对偶:最大化 \(b ^ Ty\),要求 \(A ^ Ty\leq c\)\(y\geq 0\)

\[\forall i\in [1, m],\ \sum_{j = 1} ^ n a_{ij} x_j\geq b_i \]

根据 \(y_i\geq 0\)

\[\forall i\in [1, m],\ y_i\sum_{j = 1} ^ n a_{ij} x_j\geq y_ib_i \]

\[\sum_{j = 1} ^ n x_j \sum_{i = 1} ^ m y_i a_{ij} \geq \sum_{i = 1} ^ m y_ib_i \]

\[\sum_{i = 1} ^ m y_i a_{ij} \leq c_j \]

所以

\[\sum_{j = 1} ^ n x_jc_j\geq \sum_{i = 1} ^ m y_ib_i \]

LP-duality Theorem

若原始线性规划问题的最优解空间有限,则其对偶问题的最优解空间有限。若 \(x ^ *\)\(y ^ *\) 最优,则

\[\sum_{j = 1} ^ n c_jx_j ^ * = \sum_{i = 1} ^ m b_iy_i ^ * \]

证明略。

Complementary Slackness Conditions(互补松弛条件)

\(x, y\) 最优当且仅当:

\[\forall j, \ x_j = 0 \lor \sum_{i = 1} ^ m a_{ij}y_i = c_j \]

\[\forall i, \ y_i = 0\lor \sum_{j = 1} ^ n a_{ij} x_j = b_i \]

根据 \(\sum_{j = 1} ^ n x_jc_j\geq \sum_{i = 1} ^ m y_ib_i\) 的证明过程和 LP-duality Theorem 易得。如果条件不满足,自然无法取等。

对集合覆盖,\(x_j = 1\implies \sum_{i = 1} ^ m a_{ij}y_i = c_j\),所以每次选一个 \(y_e\) 增加,直到满足某个 \(\sum_{e\in S_k} y_e = c(S_k)\),此时将 \(S_k\) 加入 \(I\)。这样每个点都被覆盖,否则其对应的 \(y_e\) 还能增加。同时解为 \(f\)-近似:

\[\sum_{S_j\in I} c(S_j) = \sum_{S_j\in I} \sum_{e\in S_j} y_e\leq f\sum_{e\in S_j} y_e \leq f\cdot \operatorname{OPT}_{\operatorname{LP}} \leq f\cdot \operatorname{OPT}_{\operatorname{IP}} \]

这是纯组合的算法,无需求解 LP。

posted @ 2024-09-12 17:43  qAlex_Weiq  阅读(2130)  评论(9编辑  收藏  举报