【算法】并查集

并查集基础:

查找、路径压缩、合并

int fa[maxn];
int find(int x){
if(fa[x] == x) return x;
else return fa[x] = find(fa[x]);
}
void union(int x, int y){
fa[find(x)] = find(y);
}

带权并查集:

查找+路径压缩:

递归向上查找父亲,向下计算权值

int find(int x){
if(fa[x] == x) return x;
else{
int tmp = fa[x];
fa[x] = find(fa[x]);
val[x] += val[tmp];
return fa[x];
}
}

合并:

// x, y 之间建立权为d的父亲边
fx = find(x);
fy = find(y);
if(fx != fy){
fa[fx] = fy;
val[fx] = -val[x] + val[y] + d;
}
// 验证 x,y 之间的权值是否为 d
else{
if(val[x] - val[y] != d){
cnt++; // 不符合要求的个数
}
}

例题:

经典带权并查集:HDU3038

题目大意:

现给出n个数字组成的序列,编号为1~n;
给出m个查询,每个查询的答案由a,b,s三个数组成,表示从第a个数加到第b个数的和为s;
但是其中有一些是有矛盾的(或者说错误的),求错误的查询答案有多少个。

pCqFleU.jpg

if(sum[A] - sum[C] != val) cnt++;

A、B 与 C、D 两个集合连接起来,即合并

pCqFUQx.jpg

fa[D] = B;
sum[D]=sum[A]+val-sum[C];

AC代码:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define PII pair<int, int>
using namespace std;
const ll maxn = 2e5 + 10;
int fa[maxn], sum[maxn];
int find(int i){
if(fa[i] == i) return i;
int tmp = fa[i];
fa[i] = find(fa[i]);
sum[i] += sum[tmp]; // 路径压缩,并且需要所有上面节点的值,与普通并查集不一样的点
return fa[i];
}
int main(){
int n, m;
while(scanf("%d%d", &n, &m) != EOF){
int ans = 0;
for(int i = 0; i <= n; i++){
fa[i] = i;
sum[i] = 0;
}
for(int i = 0; i < m; i++){
int a, b, s;
scanf("%d%d%d", &a, &b, &s);
a--;
int aa = find(a);
int bb = find(b);
if(aa != bb){
fa[bb] = aa;
sum[bb] = sum[a] + s - sum[b];
}
else{
if(sum[b] - sum[a] != s) ans++;
}
}
printf("%d\n", ans);
}
return 0;
}

本文作者:shikean@Tianjin University

本文链接:https://www.cnblogs.com/keanshi/p/17572418.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   KeanShi  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起