边带权并查集简介:
并查集实际上是由若干课树构成的森林,我们可以在树中的每条边上记录一个权值,即维护一个数组 d,用 d[x] 保存节点 x 到父节点 fa[x] 之间的边权。
在每次路径压缩后,每个访问过的节点都会直接指向树根,如果我们同时更新这些节点的 d 值,就可以利用路径压缩过程来统计每个节点到树根之间的路径上的一些信息。这就是所谓的“边带权”的并查集。
——摘录自蓝书
组成:
在普通并查集的基础上多了一个 d 数组来存储边权
fa 数组、d 数组和 size 数组
fa[x]:节点 x 的父节点
d[x]:节点 x 到父节点 fa[x] 之间的边权
size[x]:以节点 x 为树根的子树大小
初始化操作:
自己到自己的距离,初始化为0
void init(int n)
{
for (int i = 1; i <= n; i++) {
fa[i] = i;
d[i] = 0;
size[i] = 1;
}
}
能够对 d 数组进行维护的 find 操作:
d[x]:节点 x 到父节点 fa[x] 之间的边权
路径压缩后,更新节点 x 的边权 d[x]
int find(int x)
{
if (x == fa[x]) return x;
int root = find(fa[x]);
d[x] += d[fa[x]];
return fa[x] = root;
}
能够记录集合大小的 unite 操作:
合并后更新 d[x],注意对 d[x] 的更新方式并不是一成不变的,视具体情况而定
void unite(int x, int y)
{
x = find(x), y = find(y);
fa[x] = y, d[x] = size[y];
size[y] += size[x];
}
银河英雄传说
一道比较简单的带权并查集题目
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 3e4 + 5;
int fa[N], d[N], size[N];
void init(int n)
{
for (int i = 1; i <= n; i++) {
fa[i] = i;
d[i] = 0;
size[i] = 1;
}
}
int find(int x)
{
if (x == fa[x]) return x;
int root = find(fa[x]);
d[x] += d[fa[x]];
return fa[x] = root;
}
void unite(int x, int y)
{
x = find(x), y = find(y);
fa[x] = y, d[x] = size[y];
size[y] += size[x];
}
int main(void)
{
int t, i, j;
char op[2];
scanf("%d", &t);
init(N - 5);
while (t--) {
scanf("%s%d%d", op, &i, &j);
if (op[0] == 'M') {
unite(i, j);
} else {
if (find(i) == find(j)) {
printf("%d\n", abs(d[i] - d[j]) - 1);
} else {
printf("-1\n");
}
}
}
return 0;
}