瓶颈路,Kruskal 重构树及杂题选讲
引流更多知识
瓶颈路
在最小生成树有关的题目中,常见到这样一句话:
使两点之间路径经过的边权的最大值最小(最小值最大)。
这可以很形象的比喻为一个类似瓶径的东西,路径经过边的边权都要小于这条瓶径边的边权,我们要让这个瓶径最小。
于是我们引出图上一些关于瓶经的东西。以下以最小瓶颈作例:
-
最小瓶颈路:定义图上 \(u,v\) 两点的最小瓶颈路为,\(u \leadsto v\) 的所有路径中,最大边权最小的那一条。
-
最小瓶颈生成树:定义无向图的最小瓶颈生成树为这个无向图的生成树集合当中,最大边权最小的那棵生成树。
这两个东西有下面一些性质:
-
"一个生成树是一个最小生成树" 是 "这个生成树是最小瓶额生成树" 的充分不必要条件。
说人话就是如果最小生成树一定是这个图的最小瓶颈生成树,但反过来说是不对的。 -
任意不重合的两点在最小瓶颈生成树上的简单路经即这两点在原图上的最小瓶颈路的其中一条。
性质一可以通过反证法证明:简证如下:
设最小生成树的最大边权为 \(w\)
如果最小生成树不是瓶颈生成树,则瓶颈生成树的所有边权都小于 \(w\)
- 那么瓶颈生成树就是最小生成树,有矛盾,得证.(但这是伪证)
若删去原最小生或树中的最长边.用瓶颈生成树的一条边来连接删去边后形成的两子树
得到的新生成树一定比原最小生成树的权值和还要小
此时有矛盾,故性质得证。(这样是对的)
性质二应该很显然了,那我就不证了 /fad
类比最小瓶颈路的定义,我们也可以得到最大瓶颈路,最大瓶颈生成树的定义与性质。这样,如果题目要求两点之间路径经过的边权的最大值最小,先把原图的最小生成树求出来,再以边权维护一个最大边权的倍增,跑一下即可。
但我们还想要更多,我们想知道:在经过的边的边权不超过给定值的条件下,有多少个点间可以相互到达?
这个问题的解决仍然要归功于 Kruskal 算法的良好性质。为此我们引入一种新的数据结构:Kruskal 重构树。
Kruskal 重构树
Kruskal 重构树的构建过程是这样的:我们在枚举边从小到大尝试用边合并两个点集的过程中,如果一条边可以加入合并,就建立一个点权为这条边权的虚点,代表合并后的整个点集,将该点与代表两个小点集的点互相连边,建出的一定是一棵二叉树,我们称此树为 Kruskal 重构树。一般我们让最后一个建边的虚点作为该树的根。
以下是 Kruskal 直构树的一个示例。
建树的过程还是蛮有意思的,容易观察到这棵树上的性质:
- 根节点代表着原图的最小生成树中,边权最大的那一条边。
- 任意一点到根节点的简单路径上,经过点的点权是单调的。
- 以任意一点为根的子树,子树内除叶子节点外的点的点权均小于子树根的。
- 原图中两个点之间的所有简单路径上最大边权的最小值即为 Kruskal 重构树上两点之间 LCA 的权值。
回到我们的问题.从上文的性质中不难看出(长难句警告),到点 \(x\) 的简单路径上 最大边权的最小值 小于等于某固定值 的所有点 均在以 \(x\) 到根的路径上 权值小于等于某固定值的 最浅的节点 为根的子树上,且为该子树的所有叶子节点,于是我们的问题就可以解决了。
且由于 Kruskal 重构树的树形结构,我们之间提到过的在树上的小技巧也可以用上。
上文仅是以最小生成树的 Kruskal 重构树举例,最大生成树的其实一样啦。
来看些倒题吧!
问题时间
LOJ #137. 最小瓶颈路(加强版)
题目描述
给定一个无向连通图,包含 \(n\) 个点和 \(m\) 条带正权边(可能有重边)。处理 \(q\) 次询问,每次查询两点 \(u\) 和 \(v\) 之间所有路径中,路径上最大边权的最小值。数据范围
- \(n \leq 70,000\)
- \(m \leq 100,000\)
- \(q \leq 10^7\)
当然是把原图的最小生成树的 Kruskal 重构树建出来。然后在重构树上操作,但是询问量实在太大了,查询必须是常数小的 \(O(1)\),但 \(O(n \log n)\) 预处理还是可以的,于是我们用一个 ST 表求 LCA 即可,你说你不会 ST 表 LCA?
然后就是注意答案的输出方式。
code
Show me the code
P4768 [NOI2018] 归程
题目描述
给定一个无向连通图,每条边有长度 \(l\) 和海拔 \(a\)。每天给出起点 \(v\) 和水位线 \(p\),只能通过海拔严格大于 \(p\) 的边乘车,其余边需要步行。求从 \(v\) 到 \(1\) 号点的最小步行总长度(即乘车能到达离 \(1\) 号点最近的位置,再从该位置步行到 \(1\) 号点的路径总长度)。强制在线说明
询问参数 \(v\) 和 \(p\) 需要根据前一次询问的答案 \(\mathrm{lastans}\) 计算得到:
- \(v = (v_0 + K \times \mathrm{lastans} - 1) \bmod n + 1\)
- \(p = (p_0 + K \times \mathrm{lastans}) \bmod (S + 1)\)
其中 \(K \in \{0,1\}\) 表示是否强制在线。数据范围
- 数据组数 \(T \leq 3\)
- 点数 \(n \leq 2 \times 10^5\)
- 边数 \(m \leq 4 \times 10^5\)
- 询问数 \(Q \leq 4 \times 10^5\)
- 边长度 \(l \leq 10^4\)
- 海拔 \(a \leq 10^9\)
- 最高水位线 \(S \leq 10^9\)
(SPFA 葬于此题讨论区下)
你说你上来放一个 NOI 的题?其买这题的 motivation 比较明显,如果你会 Kruskal 重构树应该是能看出来的。
首先由于开车是没有代价的,于是我们大可以开着车在城里乱逛,开到一个最优的点.也就是距 \(1\) 最近的点再下车走。
然后显然海拔高的点更不易被淹,且边无向,那么我们直接考虑那些大的,可以把各点连通的那些边,哎这不就是原图的最大生或树吗。
现在你就可以想离线做法了,主要的问题就是有些边是不能被加入生成树的,这意味着在某一天是图上可能只是几个较长边组成的森林,于是我们根据水位线的高度离线,从高度高到高度低处理询问。随着水位线的降低,有一些原来被淹没的高边可以被加入最小生成树了,对应着构建最大生成树的 Kruskal 过程。选出没被淹没的高边加入 MST 即可。
现在与出发点在同一个连通块内的点就是可以开车到达的点,于是我们预处理出每个点到 \(1\) 的最短路,相当于从 \(1\) 开始的单源最短路,然后选择一个最小的点就可以了,最小的点可以在合并过程中维护。
然后想强制在线,这要求我们快速求解出在经过的边的边权不超过给定值的条件下,有多少个点间可以相互到达,哎呀这不就是 Kruskal 重构树干的事吗,于是我们拍上一个重构树,找出发点点权不小于给定值的 LCA,区间求叶子的最短路的最小值就做完啦。
code
Show me the code
P4197 Peaks
题目描述
给定一个包含 \(n\) 座山峰的无向图,每座山峰有高度 \(h_i\),\(m\) 条双向路径各有一个困难值。处理 \(q\) 次询问,每次查询从山峰 \(v\) 出发,仅通过困难值 \(\leq x\) 的路径能到达的山峰中,高度第 \(k\) 大的山峰高度(不存在则输出 \(-1\))。数据范围
- 山峰数 \(n \leq 10^5\)
- 路径数 \(m \leq 5 \times 10^5\)
- 询问数 \(q \leq 5 \times 10^5\)
- 高度和困难值 \(h_i, c, x \leq 10^9\)
输出要求
对每个询问输出符合条件的第 \(k\) 大高度或 \(-1\)
只能经过困难值小于等于 \(x\) 的路径就与 Kruskal 重构树专业对口了,我们建出原图最小生成树的 Kruskal 重构树,找到 \(v\) 的祖先中点权不大于 \(x\) 的最浅节点。现在问题即为求解这些点中叶子权的第 \(k\) 小。
因为这些点在同一个子树上,我们自然想到转化到 DFN 上去。我们在 Kruskal 重构树上用 DFS 给每个叶子节点标上 DFN,预处理出每个点代表的叶子的 DFN 区间。这样每个点对应的区间一定是连续的,于是我们把叶子权对应到叶子的 DFN 序列上,用权值线段树维护区间第 \(k\) 小即可。
code
Show me the code
AGC002D Stamp Rally
题目描述
给定一个 \(N\) 个点 \(M\) 条边的无向图,处理 \(Q\) 次询问。每次询问给出三个整数 \(x_i, y_i, z_i\),要求找到一个最小的边权 \(w\),使得在仅保留边权 \(\leq w\) 的边时,\(x_i\) 和 \(y_i\) 所在的连通块大小之和 \(\geq z_i\)。数据范围
- 点数 \(2 \leq N \leq 10^5\)
- 边数 \(1 \leq M \leq 10^5\)
- 询问数 \(1 \leq Q \leq 10^5\)
输出要求
对每个询问输出满足条件的最小边权 \(w\)
题目要求最大值最小,非常的二分答案.我们二分一个答案 \(k\),在原图的 Kruskal 重构树上信增找各自的点权不太于 \(k\) 的深度最小的点,计算两点下领的叶子树数量即为这两人可以到达的点的数量,这里也是用的 Kruskal 重构数性质(似乎这个性质特别容易忘?)基于此二分即可。注意要判断两点重合的情况来特殊处理。
code
Show me the code
P3684 Hangar Hurdles
题目描述
给定一个 \(n \times n\) 的网格图,每个格子是空地(.)或障碍物(#)。定义尺寸为奇数 \(k\) 的集装箱是以某格子为中心的 \(k \times k\) 正方形。处理 \(q\) 次询问,每次给定两个不同空地坐标 \((A_x,A_y)\) 和 \((B_x,B_y)\),求能从 \(A\) 移动到 \(B\) 的集装箱的最大奇数尺寸(无法移动时输出 \(0\))。数据范围
- 网格尺寸 \(2 \leq n \leq 1000\)
- 询问数 \(1 \leq q \leq 3 \times 10^5\)
- 保证询问的起点和终点均为空地且不相同
输出要求
对每个询问输出满足条件的最大奇数 \(k\)(不存在解则输出 \(0\))
下面是口胡喵,不知道复杂度对不对。
首先我们如果知道每个点上可以放的最大的集装箱的大小,问题就变成了求解 \(A\) 到 \(B\) 的所有合法路径中,格子最小值最大的那个,非常 Kruskal 重构树。现在我们要求每个点上可以放的集装箱大小。
从每个点都开始 \(O(n^2)\) 的扩展肯定不行,我这里口胡一个收缩和扩张的思路。
定义三个函数,分别表示扩张,收缩,不动。返回值为当前能否做到对应的操作,传入值为待检测点的坐标与相邻点集装箱大小的\(\max\)。
这三个函数怎么用呢?我们采用 BFS 的方式扩展并计算每个节点,在扩展时记录与被扩展节点相邻的点中可放集装箱大小最大的那个,到达扩展点时,首先尝试能否保持现状,即检测自己最外侧一圈是否有遮挡,注意这里仅需检测最外圈就可以了,原因是因为相邻的点可以到达这个大小,向某个方向移动一格之后只可能会在最外圈出现阻挡。若可以则不断调用扩展操作尝试扩展大小,若不可以则不断调用收缩以断尾求全。完成后即得此点可放置集装箱的大小。
似乎这里可以证明扩张,收缩操作仅需执行一次?总之这个算法时间是个上界松的 \(O(n^3)\),应该是能过的(
code
Show me the code
CF1628E Groceries in Meteor Town
题目描述
给定一棵包含 \(n\) 个节点的树,每条边有危险值 \(w_i\)。处理 \(q\) 次操作:
- 将编号 \([l,r]\) 的节点标记为"开放"
- 将编号 \([l,r]\) 的节点取消标记
- 查询节点 \(x\) 到所有开放节点的路径中,最大边危险值的最大值(若无开放节点或路径无边则返回 \(-1\))
数据范围
- 节点数 \(2 \leq n \leq 3 \times 10^5\)
- 操作数 \(1 \leq q \leq 3 \times 10^5\)
- 边权 \(1 \leq w_i \leq 10^9\)
输出要求
对每个类型3操作输出查询结果
首先把点权排序之后在 Kruskal 重构树上搞搞映射什么的这类方法是不行的,因为你无法保证排序后点的顺序就是 Kruskal 重构树上的顺序,即使你可以交换两个儿子。
这样一来,我们就不能对输入的区间做其它操作了。但我们可以用线段树来维护点的开闭情况之类的东西。
再次考虑题目中的性质,从出发点到各点只有一条简单路径,只能计算简单路径上的值。
还是转化到 Kruskal 重构树 LCA 的性质上去.在查询操作时,我们计算出发点与所有已开放的点的 LCA,这就是这些点两两间的路径中最大危险度最大的那一个。由于是一棵树,我们肯定可以找到一条从起点到开放点,且经过该边的简单路径,于是这个 LCA 的权值其实就是我们的答案。
找许多点 LCA 的工作可以由 ST 表 LCA 轻松完成。
因为是 ST 表维护 LCA,仅需维护各开放点的 DFN 的最大值,这也可以用线段树搞定。
要先建出 Kruskal 重构树!即使原图已经是一棵树。
*3100 诈骗题
code
Show me the code
CF1706E Qpwoeirut and Vertices
题目描述
给定一个 \(n\) 个节点 \(m\) 条边的连通无向图,边按输入顺序编号为 \(1\) 到 \(m\)。处理 \(q\) 次查询,每次给定区间 \([l,r]\),求最小的 \(k\) 使得:对于所有 \(a,b \in [l,r]\),节点 \(a\) 和 \(b\) 在仅使用前 \(k\) 条边时连通。数据范围
- 测试用例数 \(1 \leq t \leq 1000\)
- 节点数 \(2 \leq n \leq 10^5\)(所有测试用例总和)
- 边数 \(1 \leq m \leq 2 \times 10^5\)(总和)
- 查询数 \(1 \leq q \leq 2 \times 10^5\)(总和)
输出要求
对每个查询输出满足条件的最小 \(k\)
类似上题的思路,我们把区间里所有点在以边编号为权的最小 kruskal 重构树上的 LCA 找出来,这就是区间内各点间简单路径中.所需边编号最小值最大的边编号.与 \(k\) 比较即可。
code
Show me the code

浙公网安备 33010602011771号