我将描述该问题的多对数解决方案。让我们引入一些定义。我们用V
表示图的顶点集,用E
表示图的边集,用T
表示最小生成树边集。
- 图的顶点集为
V
,图的边集为E
,最小生成树边集为T
。
- 两个顶点
v
和u
之间的边为{v, u}
。
- 边
e
的权值为W(e)
,最小生成树的权值为W(T)
。
让我们考虑函数MaxEdge(v, u)
,它等于在从v
到u
的简单路径上属于T
的边中具有最大权重的边。如果有几条最大权重的边,则MaxEdge(v, u)
可以等于其中任何一条。
要找到第二好的最小生成树,我们需要找到这样的边x = {p, q}
,满足:
x
不属于 T
。
- 函数
W(x) - W(MaxEdge(p, q))
最小。
可以证明,次优最小生成树可以通过从 T
中删除 MaxEdge(p, q)
,然后将 x = {p, q}
添加到 T
中来构建。
现在让我们构建一个数据结构,能够以 O(log|V|)
的时间计算 MaxEdge(p, q)
。
我们为树 T
选择一个根节点(可以是任何顶点)。我们称顶点 v
到根之间的简单路径上的边数为顶点 v
的深度,并将其表示为 Depth(v)
。我们可以通过 深度优先搜索 在 O(|V|)
的时间内计算出所有顶点的 Depth(v)
。
让我们计算两个函数,这将帮助我们计算 MaxEdge(p, q)
:
Parent(v, i)
,表示顶点v
的深度为Depth(v)-2^i
的父节点(可能是非直接父节点)。
MaxParentEdge(v, i)
,表示MaxEdge(v, Parent(v, i))
。
这两个函数都可以通过带有记忆化技术的递归函数计算,并且时间复杂度为O(|V|log|V|)
。
Vertex Parent(Vertex v, Vertex i) {
if (i == 0) return direct_parent[v];
if (Memorized(v, i)) return memorized_parent[v][i];
memorized_parent[v][i] = Parent(Parent(v, i - 1), i - 1);
return memorized_parent[v][i];
}
Edge MaxParentEdge(Vertex v, Vertex i) {
if (i == 0) return Edge(v, direct_parent[v]);
if (Memorized(v, i)) return memorized_parent_edge[v][i];
Edge e1 = MaxParentEdge(v, i - 1);
Edge e2 = MaxParentEdge(Parent(v, i - 1), i - 1);
if (W(e1) > W(e2)) {
memorized_parent_edge[v][i] = e1;
} else {
memorized_parent_edge[v][i] = e2;
}
return memorized_parent_edge[v][i];
}
在我们准备计算MaxEdge(p, q)
之前,让我们介绍最终的定义。 Lca(v, u)
表示根据有根树T
中顶点v
和u
的最近公共祖先。 有很多众所周知的数据结构可以在O(log|V|)
甚至O(1)
内查询Lca(v, u)
(您可以在Wikipedia上找到文章列表)。
为了计算
MaxEdge(p, q)
,我们需要将从
p
到
q
的路径分成两部分:从
p
到
Lca(p, q)
和从
Lca(p, q)
到
q
。这两部分都类似于从一个顶点到其某些父级的路径,因此我们可以使用
Parent(v, i)
和
MaxParentEdge(v, i)
函数来计算这些部分的
MaxEdge
。请注意保留 html 标签。
Edge MaxEdge(Vertex p, Vertex q) {
Vertex mid = Lca(p, q);
if (p == mid || q == mid) {
if (q == mid) return QuickMaxEdge(p, mid);
return QuickMaxEdge(q, mid);
}
Edge e1 = QuickMaxEdge(p, mid);
Edge e2 = QuickMaxEdge(q, mid);
if (W(e1) > W(e2)) return e1;
return e2;
}
Edge QuickMaxEdge(Vertex v, Vertex parent_v) {
Edge maxe = direct_parent[v];
string binary = BinaryRepresentation(Depth(v) - Depth(parent_v));
for (int i = 0; i < binary.size(); ++i) {
if (binary[i] == '1') {
Edge e = MaxParentEdge(v, i);
if (W(e) > W(maxe)) maxe = e;
v = Parent(v, i);
}
}
return maxe;
}
基本上,函数
QuickMaxEdge(v, parent_v)
跳跃长度为
2^i
来覆盖
parent_v
和
v
之间的距离。在跳跃过程中,它使用预先计算好的
MaxParentEdge(v, i)
的值来更新答案。
考虑到
MaxParentEdge(v, i)
和
Parent(v, i)
都是预先计算好的,
MaxEdge(p, q)
的时间复杂度为
O(log|V|)
,这将导致初始问题的
O(|E|log|V|)
解决方案。我们只需要迭代所有不属于
T
的边,并为每个边计算
W(e) - MaxEdge(p, q)
即可。