Kruskal 重构树
Kruskal 重构树
1.概念
在进行Kruskal算法求解最小生成树时,添加若干虚点,使求得的树成为二叉树,二叉树的叶子节点为原图中存在的节点,且每个虚点都有一个权值,为左子树中的点到右子树中的点的简单路径的最大边权。
2.实现方法
仍然按照Kruskal算法按照边权从小到大加入边:
1.新建n个集合,每个集合图中为一个节点,点权为0.
2.按照边权从小到大顺序遍历边。
3.判断当前这条边的两个顶点是否在同一集合中,若不是,则新建一个虚点,点权为当前边的权值,左右儿子分别为这两个集合的根节点,然后将两个集合合并,设其根节点为新添加的这个点。
3.几点性质和应用
-
重构树有\(n\)个叶子节点,则此二叉树的内点为\(n - 1\)个,整个二叉树节点数为\(2n - 1\)
-
原生成树中两个节点之间简单路径的最大边权即为重构树中这两个点的最近公共祖先。
-
假设给定某个值,求从某个点出发只通过边权小于等于这个值的边可以到达的定点数,可以从这个顶点不断往上跳找到最后一个权值小于等于这个值的虚点,其子树的叶节点树即为答案。使用树上倍增可以压缩至\(O(logn)\)
4.实现代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
struct edge {
int from, to, w;
friend bool operator < (const edge &x, const edge &y) {
return x.w < y.w;
}
} edg[N];
vector <int> e[N << 1];
int fa[N << 1];
int nodetot, n, root, val[N << 1];
void add(int x, int y) {
e[x].push_back(y);
}
int find(int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
void kruskal() {
for(int i = 1; i <= 2 * n; i++) {
fa[i] = i;
}
nodetot = n;
sort(edg + 1, edg + n);
for(int i = 1; i < n; i++) {
int fa1 = find(edg[i].from), fa2 = find(edg[i].to);
if(fa1 != fa2) {
nodetot++;
val[nodetot] = edg[i].w;
fa[fa1] = fa[fa2] = nodetot;
add(nodetot, fa1); add(nodetot, fa2);
}
}
root = nodetot;
}