带权并查集

article/2025/8/26 23:58:25

带权并查集需要先理解一般的并查集,不明白的可自行先搜索有关内容

一般的并查集主要记录节点之间的链接关系,而没有其他的具体的信息,仅仅代表某个节点与其父节点之间存在联系,它多用来判断图的连通性,如下图所示,这是一个并查集,其中箭头表示父子关系,可以看到这些边没有记录其他的任何信息。

而有的时候在这些边中添加一些额外的信息可以更好的处理需要解决的问题,在每条边中记录额外的信息的并查集就是带权并查集。

不过在此之前先来看看并查集的路径压缩

在上边这个并查集中,如果对节点C做Find操作,最终会得到A,但是查找的过程中会先经过B,再通过Find(B)得到A,这是一个值得优化的地方,如果直接让C链接到A不是更好吗,这样就可以省去中间的操作,如果C跟A直接相隔很多节点,这个优化就极大地提升了查找的效率,也就是希望得到这样的结果:

将每个节点直接与其Find()操作最终得到的节点链接,就是所谓的路径压缩,这一操作可以直接在Find中完成,看下边的代码:

int find(int x)
{if (x != parent[x]){parent[x] = find(parent[x]);}return parent[x];
}

与一般的并查集相比,它只是在find(parent[x])前边加了一步赋值操作,将在查找过程中遇到的所有的节点的父节点都设为最终得到的那个节点。

基于路径压缩,带权并查集就是:

可以看到它的每一条边都记录了每个节点到根节点的一个权值,这个权值该设为什么由具体的问题而定,一般都是两个节点之间的某一种相对的关系,但是考虑到权值就会有两个问题:

1.每个节点都记录的是与根节点之间的权值,那么在Find的路径压缩过程中,权值也应该做相应的更新,因为在路径压缩之前,每个节点都是与其父节点链接着,那个Value自然也是与其父节点之间的权值

2.在两个并查集做合并的时候,权值也要做相应的更新,因为两个并查集的根节点不同。

下面就来看在这两个过程中,如何更新权值:

路径压缩:

int find(int x)
{if (x != parent[x]){int t = parent[x];parent[x] = find(parent[x]);value[x] += value[t];}return parent[x];
}

可以看到更新权值只多了两行代码,先记录下原本父节点的编号,因为在路径压缩后父节点就变为根节点了,再将当前节点的权值加上原本父节点的权值,此时父节点的权值已经是父节点到根节点的权值了,因此加上这个权值就会得到当前节点到根节点的权值。

 

合并:

		int px = find(x);int py = find(y);if (px != py){parent[px] = py;value[px] = -value[x] + value[y] + s;}

现在是已知x所在的并查集根节点为px,y所在的并查集根节点为py,如果有了x、y之间的关系,要将px并到py上,如果不考虑权值直接修改parent就行了,但是现在是带权并查集,必须得求出px与py这条边的权值是多少,很显然x到py两条路径的权值之和应该相同,就不难得出上面代码所表达的更新式。但是需要注意并不是每个问题都是这样更新的,有时候可能会做取模之类的操作,这一点在后边的例题中可以体现。

 

以上便是带权并查集所需要理解的操作,下面来看三个具体的例子:

HDU-3038-How Many Answers Are Wrong

这个题题目的废话比较多,这里简述一下大意:有M个数,不知道它们具体的值,但是知道某两个数之间(包括这两个数)的所有数之和,现在给出N个这样的区间和信息,需要判断有多少个这样的区间和与前边已知的区间和存在矛盾。例如给出区间和[1,4]为20,[3,4]为15,再给出[1,2]为30,显然这个[1,2]的值就有问题,它应该为20-15=5。

这个题目需要分析什么时候会产生矛盾,不妨就具体看看上面的这个矛盾的例子

在这里这个图是画不了的,因为使用的都是闭区间,闭区间会加上端点的值,这就提醒我们这个题在处理的时候应该将闭区间的某一端变成开区间,比如将[1,4]变成(0,4],将[3,4]变成(2,4],将[1,2]变成(0,2]

如果已知(2,4],(0,4],那么(0,2]的值就可以求出来,如果说再给出(0,2]的值,而又不与(0,2]求出来的值相等,就产生了矛盾,这是矛盾产生的唯一情况:某个区间的值可以由前边的区间求出来,而又给出了这个区间错误的值。注意,有的地方对这个题的分析存在很大的误解,有的博客中说如果已知[1,10]为100,[1,5]为1000,[1,5]的值不可能大于100所以矛盾,这是不对的,因为本题题目中没有说所有的数都是正数,因此矛盾只有在不相等的时候产生。

那么需要考虑什么时候一个区间的值可以由之前的区间求出来呢?观察上边这张图,红色的这条边是求不出来的,而绿色的是可以求出来的,为什么呢?因为0、2属于同一个并查集,0、3不属于同一个并查集,因此不难得出本题的求解思路:如果给定区间的两个端点属于同一个并查集,判断这个区间的值是否与计算得到的值相等;如果给定区间的两个端点不属于同一个并查集,将这两个并查集合并。并查集边的权值就等于题干中的区间和。

 

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <stdio.h>
using namespace std;const int maxM = 200005;
int parent[maxM];
int sum[maxM];int Find(int x)
{if (x != parent[x]){int i = parent[x];parent[x] = Find(parent[x]);sum[x] += sum[i];}return parent[x];
}int main()
{int m, n;int ans = 0;while (scanf("%d%d", &m, &n) != EOF){for (int i = 0; i <= m; i++){parent[i] = i;sum[i] = 0;}ans = 0;while (n--){int l, r, value;cin >> l >> r >> value;l--;int fl = Find(l);int fr = Find(r);if (fl == fr){if ((sum[l] - sum[r]) != value){ans++;}}else {parent[fl] = fr;sum[fl] = -sum[l] + sum[r] + value;}}cout << ans << endl;}return 0;
}

由此可见带权并查集实际上就类似于前缀和,通过已知的两个相对信息求出一个绝对信息。

 

HihoCoder-1515-分数调查

描述

小Hi的学校总共有N名学生,编号1-N。学校刚刚进行了一场全校的古诗文水平测验。  

学校没有公布测验的成绩,所以小Hi只能得到一些小道消息,例如X号同学的分数比Y号同学的分数高S分。  

小Hi想知道利用这些消息,能不能判断出某两位同学之间的分数高低?

输入

第一行包含三个整数N, M和Q。N表示学生总数,M表示小Hi知道消息的总数,Q表示小Hi想询问的数量。  

以下M行每行三个整数,X, Y和S。表示X号同学的分数比Y号同学的分数高S分。  

以下Q行每行两个整数,X和Y。表示小Hi想知道X号同学的分数比Y号同学的分数高几分。  

对于50%的数据,1 <= N, M, Q <= 1000  

对于100%的数据,1 <= N, M, Q<= 100000 1 <= X, Y <= N -1000 <= S <= 1000

数据保证没有矛盾。

输出

对于每个询问,如果不能判断出X比Y高几分输出-1。否则输出X比Y高的分数。

样例输入

10 5 3  
1 2 10  
2 3 10  
4 5 -10  
5 6 -10  
2 5 10  
1 10  
1 5  
3 5

样例输出

-1  
20  
0

这道题给的都是相对关系,显然可以用带权并查集来做,权值就是子节点比父节点多的分数,询问能求解的情况就是两个人同属于一个并查集的情况。这里不多解释了:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
const int maxN = 100005;
int parent[maxN];
int score[maxN];int find(int x)
{if (x != parent[x]){int t = parent[x];parent[x] = find(parent[x]);score[x] += score[t];}return parent[x];
}
int main()
{int n, m, q;scanf("%d%d%d", &n, &m, &q);for (int i = 1 ; i <= n ; i ++ ){parent[i] = i;}while ( m-- ){int x, y, s;scanf("%d%d%d", &x, &y, &s);int px = find(x);int py = find(y);if (px != py){parent[px] = py;score[px] = -score[x] + score[y] + s;}}while ( q -- ){int x, y;scanf("%d%d",&x,&y);if (find(x) != find(y)){printf("-1\n");}else {printf("%d\n", score[x] - score[y]);}}return 0;
}

 

poj-1182-食物链

 

食物链

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 92490 Accepted: 27910

Description

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 
有人用两种说法对这N个动物所构成的食物链关系进行描述: 
第一种说法是"1 X Y",表示X和Y是同类。 
第二种说法是"2 X Y",表示X吃Y。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 

Input

第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3

这道题之前我有记录过用一般并查集的做法:开三倍空间的并查集,显然这个题给的又都是相对关系,不能够确定具体的某个动物属于哪个物种,因此才有开三倍空间来处理的那样的思路,但是带权并查集就是用来处理相对关系的,可以考虑用带权并查集来做。

在这个题中相对关系就是食物链上的关系,因此带权并查集中的权值就应该记录两个动物在食物链上的相对关系,A->B为0表示同类,为1表示A吃B,为2表示A被B吃。这个值不同于前面两个题中的区间合、分数差,它是不可以直接累加的,要考虑三个问题:

1.路径压缩时,如何更新Value

如果现在有A->B为1,B->C为1,怎么求A->C?显然A吃B,B吃C,那么由题意C应该吃A,那么A->C应该为2;

如果现在有A->B为2,B->C为2,怎么求A->C?显然B吃A,C吃B,那么由题意A应该吃C,那么A->C应该为1;

如果现在有A->B为0,B->C为1,怎么求A->C?显然A、B同类,B吃C,那么由题意A应该吃C,那么A->C应该为1;

找规律不难发现,A->C = (A->B + B->C) % 3,因此关系值的更新需要累加再模3。

2.区间合并时,如何更新Value

由1不难发现,本题的Value更新无非就是多了个取模操作,因此不难验证区间合并的更新操作应该为:

relationWithParent[fx] = (-relationWithParent[x] + relation + relationWithParent[y]) % 3

3.如何判断是否矛盾

不同于分数、区间和,可以直接相减得到计算结果,这里要解决如果已知A与根节点的关系,B与根节点的关系,如何求A、B之间的关系?由于关系值的计算要模3,因此A->B=(A->C - B->C + 3) % 3,加三是为了避免负数的影响。将A->B与题目给的Relation值判等比较即可。

注意,本题给的relation值都是1或2,在实际处理的时候,这个值应该减1。

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <stdio.h>
using namespace std;
const int maxN = 50005;int parent[maxN];
int relationWithParent[maxN];    // 0 : 同类 1:吃 2:被吃// A吃B B吃C ==> A被C吃     Relation(a,c) = Relation(a,b)+Relation(b,c)
// A被B吃 B被C吃 ==> A吃C Relation(a,c) = (Relation(a,b)+Relation(b,c))%3
int find(int x )
{if ( x != parent[x]){int t = parent[x];parent[x] = find(parent[x]);relationWithParent[x] = (relationWithParent[x] + relationWithParent[t]) % 3;}return parent[x];
}int main()
{int n, k;cin >> n >> k;int ans = 0;for (int i = 1 ; i <= n ; i ++ ){parent[i] = i;}while (k--){int relation, x, y;scanf("%d%d%d",&relation,&x,&y);if ( x > n || y > n || (relation == 2 && x == y ) ){ans++;}else {int fx = find(x);int fy = find(y);if (fx == fy){if ((relation - 1) != ((relationWithParent[x] - relationWithParent[y] + 3) % 3))ans++;}else {parent[fx] = fy;relationWithParent[fx] = (-relationWithParent[x] + relation  - 1 + relationWithParent[y]) % 3;}}}cout << ans;return 0;
}

http://chatgpt.dhexx.cn/article/3arlTRxM.shtml

相关文章

并查集,不就一并和一查?

什么是并查集 并查集这种数据结构&#xff0c;可能出现的频率不是那么高&#xff0c;但是还会经常性的见到&#xff0c;其理解学习起来非常容易&#xff0c;通过本文&#xff0c;一定能够轻轻松松搞定并查集&#xff01; 对于一种数据结构&#xff0c;肯定是有自己的应用场景和…

数据结构——并查集

并查集是一种数据结构&#xff0c;是树的一种应用&#xff0c;用于处理一些不交集&#xff08;一系列没有重复元素的集合&#xff09;的合并以及查询问题。并查集支持如下操作&#xff1a; 查询&#xff1a;查询某个元素属于哪个集合&#xff0c;通常是返回集合内的一个“代表…

并查集详解(C/C++)

并查集算法详解&#xff08;C&#xff09; 并查集基础并查集是什么&#xff1f;并查集的作用是什么&#xff1f;并查集的结构合并查询 代码实现优化1&#xff1a;避免退化&#xff08;按秩合并&#xff09;代码优化 优化2&#xff1a;路径压缩代码优化 最终代码实现复杂度分析经…

并查集及其应用

并查集的基本操作: 1.合并两个集合 2.查询两个元素的祖宗节点 扩展: 1.记录每个集合的大小 将集合大小绑定到代表元素节点上 就是并查集的思想 (不是每个元素都是正确的 但是代表元素是正确的) 2.记录每个点到根节点的距离 绑定到每个元素身上 因为每个元素到根节点的距离…

并查集(Union-Find) (图文详解)

文章目录 并查集基础知识定义C实现优化 精选算法题(Java实现)实现并查集交换字符串中的元素最长连续序列 - 字节面试常考连通网络的操作次数最大岛屿数量 (三种解法)省份数量冗余连接冗余连接Ⅱ情侣牵手(困难)移除最多的同行或同列石头等式方程的可满足性 结语 并查集基础知识 …

什么是 “并查集” ?

导语&#xff1a;并查集是一种精巧的算法&#xff0c;本身并不难理解&#xff0c;却很常用&#xff0c;在许多场景下都能找到并查集的身影。 本文作者封承成&#xff0c;年仅12岁&#xff0c;非常感谢他的投稿。 并查集是什么 并查集&#xff0c;是一种判断“远房亲戚”的算法。…

并查集(Disjoint Set)

目录 ❤️什么是并查集&#xff1f; &#x1f3b6;实现方法1 &#x1f414;实现方法2 &#x1f383;题目1 ❤️什么是并查集&#xff1f; 并查集是一种数据结构&#xff0c;用于处理一些不交集&#xff08;Disjoint sets&#xff0c;一系列没有重复元素的集合&#xff09;的…

并查集

参考 https://www.cnblogs.com/asdfknjhu/p/12515480.html https://www.bilibili.com/video/BV13t411v7Fs?p3 https://leetcode-cn.com/problems/number-of-provinces/solution/python-duo-tu-xiang-jie-bing-cha-ji-by-m-vjdr/ 一、基本概念 并查集是一种数据结构 并查集这…

并查集(详细解释+完整C语言代码)

目录 1概论 2.树的表现形式 3.代码实现 3.1创立集合 3.2合并 3.3查询 3.4路径压缩 第一个方法&#xff1a;查找时优化 第二个方法&#xff1a;合并时优化&#xff08;加权标记法&#xff09; 4.完整代码 4.1优化前 4.2优化后 1概论 并查集是一种十分精巧且实用的树形…

时间序列预测的一般步骤

一、ARIMA模型概述 ​​ ARMA模型就是AR和MA的简单结合&#xff0c;同时包含了历史数值项和错误项。由于AR和MA模型都对时间序列有平稳性要求&#xff0c;ARMA模型也存在这个限制&#xff0c;因此我们将其拓展到ARIMA模型&#xff0c;其可以解决非平稳性问题。引入的差分概念是…

时间序列预测算法总结

时间序列算法 time series data mining 主要包括decompose&#xff08;分析数据的各个成分&#xff0c;例如趋势&#xff0c;周期性&#xff09;&#xff0c;prediction&#xff08;预测未来的值&#xff09;&#xff0c;classification&#xff08;对有序数据序列的feature提…

基于深度学习的时间序列预测

# 技术黑板报 # 第十一期 推荐阅读时长&#xff1a;15min 前言 时间序列建模历来是学术和工业界的关键领域&#xff0c;比如用于气候建模、生物科学和医学等主题应用&#xff0c;零售业的商业决策和金融等。虽然传统的统计方法侧重于从领域专业知识层面提供参数模型&#xff0…

预测类问题中的时间序列预测模型

前言&#xff1a;时间序列预测模型适用于含有时间变量的模型预测&#xff0c;对中短期的预测有着较好的结果&#xff0c;对长期预测不适用。本文重点介绍ARIMA模型的原理及代码实现。 其他模型总结&#xff1a;​【Python】Python中的经典时间序列预测模型总结_风度78的博客-C…

Transformer 在时间序列预测中的应用

2017年&#xff0c;Google的一篇 Attention Is All You Need 为我们带来了Transformer&#xff0c;其在NLP领域的重大成功展示了它对时序数据的强大建模能力&#xff0c;自然有人想要把Transformer应用到时序数据预测上。在Transformer的基础上构建时序预测能力可以突破以往的诸…

【推荐收藏】11种比较常用的时间序列预测模型

时间序列及其预测是日常工作中建模&#xff0c;分析&#xff0c;预测的重要组成部分。 本文我们将从0开始介绍时间序列的含义&#xff0c;模型及其分析及其常用的预测模型。 文章目录 交流时间序列定义时间序列预测模型与方法原始数据朴素法简单平均法移动平均法指数平滑法一次…

时间序列预测的20个基本概念总结

1、时间序列 时间序列是一组按时间顺序排列的数据点 比如&#xff1a; 每小时的气压每年的医院急诊按分钟计算的股票价格 2、时间序列的组成部分 时间序列数据有三个主要组成部分。 趋势季节性残差或白噪声 3、趋势 在时间序列中记录的长期缓慢变化/方向。 4、季节性 …

时间序列-预测(Forcasting):时间序列预测算法总结

一、背景介绍 绝大部分行业场景,尤其是互联网、量化行业,每天都会产生大量的数据。金融领域股票价格随时间的走势;电商行业每日的销售额;旅游行业随着节假日周期变化的机票酒店价格等; 我们称这种不同时间收到的,描述一个或多种特征随着时间发生变化的数据,为时间序列…

时间序列预测 | ARMA应用指南

ARMA可谓是时间序列最为经典常用的预测方法&#xff0c;广泛应有于涉及时间序列的各个领域。ARMA模型自出道以来&#xff0c;出场次数不可胜数。想必大家也都不陌生&#xff0c;常学常新&#xff0c;我们今天不妨再来回顾一遍&#xff5e;。 ARMA全称Autoregressive moving ave…

时间序列预测的7种方法

import pandas as pd#取数 #dfpd.read_csv(jetrail.csv) #print(df.head()) ID Datetime Count 0 0 25-08-2012 00:00 8 1 1 25-08-2012 01:00 2 2 2 25-08-2012 02:00 6 3 3 25-08-2012 03:00 2 4 4 25-08-2012 04:00 2#pr…

Transformer时间序列预测

介绍&#xff1a; 提示&#xff1a;Transformer-decoder 总体介绍 本文将介绍一个 Transformer-decoder 架构&#xff0c;用于预测Woodsense提供的湿度时间序列数据集。该项目是先前项目的后续项目&#xff0c;该项目涉及在同一数据集上训练一个简单的 LSTM。人们认为 LSTM 在…