文章目录
模板
首先贴上模板:
struct DSU {
vector<int> fa, dis, sz;
int n, comp_cnt;
DSU(int _n) : n(_n), comp_cnt(_n), fa(_n), dis(_n, 0), sz(_n, 1) {
iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
if (x == fa[x]) return x;
int t = fa[x];
fa[x] = find(fa[x]);
dis[x] += dis[t];
return fa[x];
}
void unite(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
if (sz[x] < sz[y]) {
swap(x, y);
}
fa[y] = x;
sz[x] += sz[y];
}
}
void unite_dis(int x, int y) {
// 把x接到y后面
x = find(x);
y = find(y);
if (x != y) {
fa[x] = y;
dis[x] += sz[y];
sz[y] += sz[x];
}
}
bool connected(int x, int y) {
return find(x) == find(y);
}
};
普通并查集一些应用
并查集判环
形成环等价于新加一条边时,这条边的两个点是否已经都在同一个并查集里了,若新添加边的两个端点都已在同一个集合内,则连通它构成一个环。
并查集合并完后判断有几个集合
int num_set = 0;
for (int i = 0; i < n; i++) {
if (fa[i] == i) {
num_set++;
}
}
带权并查集的理解
普通并查集只能简单的维护某几个元素在同一个集合内,但是对这几个元素之间的关系没有记录。带权并查集就是为了记录集合内各个元素之间的关系而产生的,其主要通过记录当前元素与根的某种关系,这种关系可以是距离,也可以是奇偶性,主要根据每个题来定。
带权并查集在合并时本质上是对向量的一种操作,这样才能完成合并时计算出根节点连接时边上的权值是多少。在合并的时候,本质上是一种元素之间关系的传递。
例如上图,现在有两个集合,现在告诉 1 1 1与 4 4 4之间的关系为 x x x,那么需要将两个集合合二为一,那么 15 ⃗ \vec{15} 15之间就有了两条路径,分别是:
13 ⃗ + 35 ⃗ (1) \vec{13}+\vec{35} \tag{1} 13+35(1)
14 ⃗ + 45 ⃗ (2) \vec{14}+\vec{45} \tag{2} 14+45(2)
v a l [ i ] val[i] val[i]代表了 i i i节点到根节点的权值。
由 ( 1 ) = = ( 2 ) (1)==(2) (1)==(2)得到 v a l [ 1 ] ( 即 13 ⃗ ) + v a l [ 3 ] = v a l [ 4 ] + x val[1](即\vec{13})+val[3]=val[4]+x val[1](即13)+val[3]=val[4]+x,即 v a l [ 3 ] = v a l [ 4 ] + x + v a l [ 1 ] val[3]=val[4]+x+val[1] val[3]=val[4]+x+val[1]。
若要求同一个集合内某两个元素之间的权值关系,也是一样。
比如这里只有一个集合,要求集合内 1 1 1与 4 4 4的权值关系,即 v a l [ 1 ] = x + v a l [ 4 ] val[1]=x+val[4] val[1]=x+val[4],所以 x = v a l [ 1 ] − v a l [ 4 ] x=val[1]-val[4] x=val[1]−val[4]。
练习题
HDU3038 - How Many Answers Are Wrong
HDU3038
给你一系列区间和,判断给出的区间中有几个是不合法的。
合并时 v a l [ a ] + v a l [ r a ] = s + v a l [ b ] val[a]+val[ra]=s+val[b] val[a]+val[ra]=s+val[b];
检查时 s + v a l [ b ] = = v a l [ a ] s+val[b]==val[a] s+val[b]==val[a];
```#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int fa[N], val[N];
int find(int x) {
if (fa[x] == x) return x;
int t = fa[x];
fa[x] = find(fa[x]);
val[x] += val[t];
return fa[x];
}
int main() {
cin.tie(0);
ios_base::sync_with_stdio(false);
int n, m;
while (cin >> n >> m) {
for (int i = 0; i <= n; i++) fa[i] = i;
memset(val, 0, sizeof(val));
int cnt = 0;
for (int i = 0; i < m; i++