在读这篇文章之前,请确保你会Kruskal(最小生成树)算法与树上LCA算法。
引入
Description
给定一棵树,边带权,每次查询两点间经过的所有边的长度的最小值。
虽然本题可以采用时间复杂度优秀的树剖+RMQ或树上倍增,但是本题也可以采用Kruskal重构树的方法解决。
Solution
首先,我们将树上的边按照权值从小到大排序。考虑建立出一棵新树:
从前往后扫一遍边。假设当前扫到的边连接了节点 u , v u,v u,v 且边权 w w w。若加入 u , v u,v u,v这条边后不会出现环(即为最小生成树的树边),那么我们就建立一个新点 p p p,将 p p p 分别连向 u , v u,v u,v 所在集合的代表,并令点 p p p 的权值为 w w w ;最后我们将 u , v u,v u,v 所在集合的代表连向 p p p 。并查集维护即可。
建立出来的这棵树被称为"Kruskal重构树"。它有一个重要的性质:
⌊ \lfloor ⌊ 这棵树是一个二叉树;更进一步的,也是一个大根堆/小根堆 ⌉ \rceil ⌉
因此,对于一次形如 ( x , y ) (x,y) (x,y) 的询问,答案为新树上 LCA ( x , y ) \text{LCA}(x,y) LCA(x,y) 的点权。
时间复杂度 O ( n log n ) O(n \log n) O(nlogn)。
例题
例1(Luogu P4197)
Description
1
≤
n
≤
1
0
5
,
1
≤
m
,
q
≤
5
×
1
0
5
,
h
i
,
c
,
x
≤
1
0
9
1\le n\le 10^5, 1 \le m,q \le 5×10^5, h_i,c,x \le 10^9
1≤n≤105,1≤m,q≤5×105,hi,c,x≤109
Solution
首先我们建立出这个图的Kruskal重构树。
不难发现,一次询问中所有满足要求的点均在以 r r r 为根的子树中。 r r r 可以通过倍增求出。
于是现在问题变为:静态查询子树中的点权第 k k k 小值。这显然可以使用 dfs 序+主席树维护,
注意 h h h 需要离散化。时间复杂度为 O ( ( n + m ) log n ) O((n+m) \log n) O((n+m)logn)。
配套练习题
提示: 关于最大生成树的重构树+图论/倍增的综合题
例2
Description
给定一棵大小为 n n n的无向图。你需要执行 q q q次询问或操作:
①1 u
: 令所有与
u
u
u连通的节点中点权最大的节点是
v
v
v,你需要将
v
v
v的点权变为
0
0
0并输出
v
v
v;
②2 x y
: 删除从
x
x
x到
y
y
y的无向边。保证这条边存在。
n , q ≤ 5 × 1 0 5 n,q \le 5 \times 10^5 n,q≤5×105,时限 2s \text{2s} 2s。
Solution
动态删边,动态查询点权的最大值……我们自然而然地想到了LCT。可惜这个东西的常数非常大;换句话说,这个东西的时间复杂度并不正确。我们需要一种常数较小的单 log \log log 做法。
考虑给每条边一个权值,表示它被删除的时刻。特别的,永远没有被删除的边的权值为 ∞ ∞ ∞ 。于是,第 i i i 次询问就变为了“查询仅经过权值不小于 i i i 的边所能到达的最小点权”。
我们建出 Kruskal 重构树,此时问题变为:单点修改,查询子树最小值位置。我们建立一棵线段树,每个节点维护其对应区间的点权最大值与点权最大值的位置。
时间复杂度 O ( ( n + m ) log m ) O((n+m) \log m) O((n+m)logm)。