#include // 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; }