Files
python/TangDou/DisjointSetUnion/HDU3038.cpp

85 lines
3.3 KiB
C++
Raw Normal View History

2025-08-30 18:35:01 +08:00
#include <bits/stdc++.h>
// http://acm.hdu.edu.cn/showproblem.php?pid=3038
// https://blog.csdn.net/sunmaoxiang/article/details/80959300
// https://blog.csdn.net/qq_41552508/article/details/88636637
// https://yiqzq.blog.csdn.net/article/details/80152561
/**
思路: 很显然带权并查集,我们可以用距离的概念代替和的概念比较好理解一点,d表示x到y的和即x到y的距离;
可以用value[x]表示x到其父亲节点的距离,将正确的距离关系合并到并查集中(错误的当然不能合并到里面啦);
要注意的是这里的x, y是闭区间, 我们不好直接处理, 要先将其变成开区间来, [x, y]等价于(x-1, y]或者[x, y+1);
还有要注意有多组输入(坑爹)......
对于这道题来说,加的权,其实就是用value数组存从i的根(并不一定是1,并有可能被更新)到i的值。
需要注意的是,a到b的这个区间,比如说([1,3],[3,7],如果将这两个数组合并,将会把value[3]算两遍.
这里我们用的是不包括前面的数的办法,即(1,3],(3,7]
题意:有一个数列,不知道数列里的数值,有多个询问,每个询问给出回答一段区间的和,但是有些是对的,有些是错的,保证如果无逻辑错误,
则该回答认为是正确的,问哪些询问是错误的可以忽略。
对于询问区间和其实可以看做是前缀和,区间和也就是前缀和的差值,
[a,b]的和就转换成 b个元素的前缀和与 a-1 个元素的前缀和的差值,因此就转变为元素差值关系问题了,
就可以用并查集与元素权值来判断,每个元素的权值代表其与根元素的值的差。注意读入 a、b 区间,需要对 a-1 和 b 进行合并。
这是并查集很常见的题型。
*/
/**
测试数据
10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1
答案:1
*/
using namespace std;
const int N = 200010;
int fa[N]; //并查集数组
int value[N]; //用一个value[]数组保存从某点到其根节点距离(权值)
int n;//区间[1,n]
int m;//下面有m组数据
int ans;
//路径压缩+修改权值 [带权并查集路径压缩模板]
int find(int x) {
if (x == fa[x])
return x;
else {
int t = fa[x]; //记录原父节点编号
fa[x] = find(fa[x]); //父节点变为根节点,此时value[x]=父节点到根节点的权值
value[x] += value[t]; //当前节点的权值加上原本父节点的权值
return fa[x];
}
}
int main() {
//读入,初始化
while (cin >> n >> m) {
for (int i = 0; i <= n; i++) {
fa[i] = i;
value[i] = 0;
}
//输入m组数据
while (m--) {
int l, r, dis;
cin >> l >> r >> dis; //l r v表示[l,r]区间和为v
l--; //这里我们用的是不包括前面的数
int RootA = find(l);
int RootB = find(r);
if (RootA == RootB) {
if (value[l] - value[r] != dis) ans++; //每输入一组数据,判断此组条件是否与前面冲突,输出冲突的数据的个数
} else {
fa[RootA] = RootB;
value[RootA] = -value[l] + value[r] + dis; //并查集的权值计算
}
}
cout << ans << endl;
}
return 0;
}