次短路 & 次小生成树 学习笔记

djwj233

2021-07-15 15:53:43

Personal

# 次小生成树 ### 1. 最小生成树 #### 1.1 最小生成树问题的求解 设有一无向连通图 $G=(V,E)$,$E$ 中边带权。其最小生成树(*Minimum Spanning Tree*)就是 $G$ 的所有**生成树**中边权和最小的一棵。 以下记 $|V|=n$,$|E|=m$。 MST 问题的求解分为**破圈法**和**避圈法**两类,以下三种均属于破圈法。 - *Prim*。最坏 $\Theta(n^2)$,主要适用于**完全图**或**稠密图**。 - *Kruskal* 。 最坏 $\Theta(m\log m)$,是最常见的求法。 - *Boruvka* 。最坏 $\Theta(m\log n)$ ,是效率最高的求法,不过要求**图中边权互不相同**。 #### 1.2 最小异或生成树 实际上,*Boruvka* 算法的思想一般用于求解**最小异或生成树**。 *Boruvka* 算法求最小生成树的主过程: 1. 建立 MST 的边集 $E^\prime$,初始时 $E^\prime=\varnothing$。 2. 根据 $E^\prime$ 求出每个点所在的连通块 $a_i$,并将每个连通块的“最小边” $b_i$ 设为 $\infty$。 3. 遍历每条边 $(u,v,w)$,若 $u$ 和 $v$ 不在同一个连通块,执行 $b_{a_u}\gets \min\{b_{a_u},w\}$。 4. 若所有连通块中 $b_i=\infty$,退出过程。否则将所有不为 $\infty$ 的 $b_i$所在的边加入 $E^\prime$,返回第 2 步。 由于每次迭代连通块数量至少减半,因此至多迭代 $\Theta(\log n)$ 次,复杂度 $\Theta(m\log n)$。 > **例题**:[CF888G](https://codeforces.com/problemset/problem/888/G)([洛谷入口](https://www.luogu.com.cn/problem/CF888G)) 显然在第 3 步时,我们不能遍历完全图中的每条边。 异或问题主要采用 *01 trie* 或 *线形基* 解决,此题显然不能用线形基,则先建一棵 *01 trie*,将所有的 $a_i$ 插入。 首先,若 $\exists i\ne j,a_i=a_j$,考虑给 $(i,j)$ 连边,边权必为 $0$。连边后, $i$ 与 $j$ 相当于同一个点,因此我们忽略这种情况。 不难发现,既有左儿子又有右儿子的节点个数为 $n-1$ 个,这 $n-1$ 个点就是**我们需要连的边在 *trie* 上所对应的点**。 *Boruvka* 算法的主过程就是**在每个已有的连通块中找出一条最短的外连边**,并连上。 在此问题中这样能尽可能减少差值,使连通块的外连边只连向它的兄弟。 现在,问题转化为:在这 $n-1$ 个点的子树中分别找出两条路径,使它们的异或和最小。 **直接找这两条路径不!是!N!方!的!!!复杂度 $O(n\log n)$!** 我在这儿卡了一年,后来发现树高至多为 $30$...... 那么贪心地把这两条路径找出即可。 [Code](https://www.luogu.com.cn/record/53224333) >复杂度证明如下: > >显然这 $n-1$ 个点互不相同,它们中最深的点高度最小为 $\Theta(\log n)$,此时个数为 $\Theta(n)$。 > >这 $n-1$ 个点中的叶子结点的子树总大小最大为 $30n-n=\Theta(n)$,其中每棵均摊被遍历到 $\Theta(\log n)$ 次。 > >因此复杂度最大为 $\Theta(n\log n)$。 ### 2. 次小生成树 次小生成树分为**严格次小生成树**和**非严格次小生成树**。 #### 2.1 非严格次小生成树 求非严格次小生成树的主要思路是**在最小生成树中找到一条边,用非树边换掉**。 1. 求出最小生成树 $T$,设边权之和为 $S$。 2. 树上倍增,预处理每个点到其 $2^i$ 级祖先的边权中最大值。 3. 从小到大遍历不在树中的所有边 $(u,v,w)$,在树上求出 $u$ 到 $v$ 路径上最大的边权 $z$,用 $S-z+w$ 更新答案。 时间复杂度 $\Theta(m\log m)$。 可以说明,若非严格次小生成树的边权和等于最小生成树的边权和,则最小生成树不唯一。 #### 2.2 严格次小生成树 实际上二者的求法思路相同,不过严格的更难求。 1. 求出最小生成树 $T$,设边权之和为 $S$。 2. 树上倍增,预处理 $j$ 号点到其 $2^i$ 级祖先 $fa_{i,j}$ 的边权中最大值 $f_{i,j}$ 和**严格次大值** $g_{i,j}$(没有则为 $-1$) 具体地,有: $$ f_{i,j}=\max\{f_{i-1,j},f_{i-1,fa_{i-1,j}}\} $$ $$ g_{i,j}=\begin{cases} \max\{f_{i-1,j},g_{i-1,fa_{i-1,j}}\}&f_{i-1,j}<f_{i-1,fa_{i-1,j}}\\ \max\{g_{i-1,j},f_{i-1,fa_{i-1,j}}\}&f_{i-1,j}>f_{i-1,fa_{i-1,j}}\\ \max\{g_{i-1,j},g_{i-1,fa_{i-1,j}}\}&f_{i-1,j}=f_{i-1,fa_{i-1,j}}\\ \end{cases} $$ 3. 从小到大遍历不在树中的所有边 $(u,v,w)$,在树上求出 $u$ 到 $v$ 路径上最大的边权 $z$。 若 $z=w$,则取 $z$ 为次大边权(依照上式方法进行合并)。再用 $S-z+w$ 更新答案。 时间复杂度 $\Theta(m\log m)$。 > **例题**:[P4180 [BJWC2010]严格次小生成树](https://www.luogu.com.cn/problem/P4180) [Code](https://www.luogu.com.cn/record/53237957) ``` #1 Input: 7 15 6 3 35 1 6 44 3 2 22 2 7 57 5 1 57 5 6 65 5 3 44 7 4 57 7 2 44 7 1 44 4 5 44 4 5 44 2 3 65 1 7 44 1 2 44 Output: 242 ``` # 次短路 ### 1. 最短路 以下均指**单源最短路**(*Single Source Shortest Path*)问题。 解法一般有: - *Bellman-Ford* 和 *SPFA*。最坏 $\Theta(nm)$,尽量不使用。 - *Dijkstra*。最坏 $\Theta(n^2)$,一般适用于完全图。 - 堆优化 *Dijkstra*,最坏 $\Theta(m\log n)$,是最常用的求法。 当然也可把堆换为斐波那契堆以获得更优复杂度。 ### 2. 次短路 同样,次短路也分为**严格次短路**和**非严格次短路**。 有两种常用做法: #### 2.1 *k* 短路 注意到非严格次短路其实就是 $k=2$ 时的情况,因此可以直接套板子。 求严格次短路也很简单,不再赘述。 #### 2.2 类似最短路的做法 其实十分简单,直接修改最短路的松弛条件即可。 ```c++ if(dis[y]>dis[x]+edge[i]) { dis2[y]=min(dis[y],dis2[x]+edge[i]); dis[y]=dis[x]+edge[i]; q.push(make_pair(dis[y],y)); } else if(dis[y]<dis[x]+edge[i] && dis2[y]>dis[x]+edge[i]) { dis2[y]=dis[x]+edge[i]; q.push(make_pair(dis2[y],y)); } else if(dis2[y]>dis2[x]+edge[i]) { dis2[y]=dis2[x]+edge[i]; q.push(make_pair(dis2[y],y)); } ``` 由于每个点最多被松弛 $2$ 遍,所以复杂度依然是 $\Theta(m\log n)$。 **例题**:[P2865 [USACO06NOV]Roadblocks G](https://www.luogu.com.cn/problem/P2865) [Code](https://www.luogu.com.cn/record/53279746) 完结撒花~~