题意:给出区间[1,n],下面有m组数据,l r v区间[l,r]之和为v,每输入一组数据,判断此组条件是否与前面冲突 ,最后输出与前面冲突的数据的个数.
看大佬的博客:https://www.cnblogs.com/liyinggang/p/5327055.html
https://blog.csdn.net/niushuai666/article/details/6981689
向量偏移好秀啊,可以直接找到根节点和子节点的关系。
对于集合里的任意两个元素x,y而言,它们之间必定存在着某种联系,因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们就把这2个元素之间的关系量转化为一个偏移量。
这一题sum[maxn]数组保存的是从一点到其祖先节点的距离。
x的当前集合根节点rootx,y的当前集合根节点rooty,x->y的偏移值为c;
(1)如果rootx和rooty不相同,那么我们把rootx合并到rooty上,并且更新sum[rootx]的值;
向量偏移 rootx->rooty = rootx->x + x->y + y->rooty = -x->rootx+ x->y + y->rooty
即sum[rootx] = -sum[x] + c + sum[y]
(2)如果rootx和rooty相同,(即x和y在已经在一个集合中,不需要合并操作了,根结点相同),那么我们就验证x->y之间的偏移量是否与题中给出的c一致
向量偏移 x->y = x->rootx + rootx->y
即sum[x] - sum[y] == c ?
sum[maxn]数组的更新在路径压缩的时候已经更新
链接:hdu 3038
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;
const int maxn = 200000 + 10;
const int inf = 1e9 + 7;
int n;
int father[maxn];
int sum[maxn];
void init(int n) {
for(int i = 0; i <= n; i++) {
father[i] = i;
sum[i] = 0;
}
}
// 获取根结点
int getf(int x) {
if(father[x] != x) {
int t = father[x];
father[x] = getf(father[x]);
sum[x] += sum[t];
}
return father[x];
}
int main()
{
int T, kcase = 0;
int m;
while(~scanf("%d %d", &n, &m)) {
init(n);
int ans = 0;
int a, b, c;
while(m--) {
scanf("%d %d %d", &a, &b, &c);
a--;
int roota = getf(a);
int rootb = getf(b);
if(roota == rootb) {
if(sum[a] - sum[b] != c) {
ans++;
}
}
else {
father[roota] = rootb;
sum[roota] = sum[b] - sum[a] + c;
}
}
cout << ans << endl;
}
return 0;
}