题目链接:[HDU 3038]How Many Answers Are Wrong[并查集]
题意分析:
给出区间值的和,问这些话中有多少个是和之前矛盾的。
解题思路:
用一个dis数组记录点到根节点的距离(也就是从该节点到根节点的整个区间和),那么当所给出的区间两点的根节点相同时,就可以进行判断。例如这两个区间端点是l和r。那么整个区间的总和就是dis[l] - dis[r]。由于区间l,r是闭区间,所以在这里我们记录的时候让l--,这样减出来的区间就是所求的区间。当根节点不同时,就对其进行合并,默认左边并到右边。具体见代码~
个人感受:
这一类型的并查集题目卡了好几天了,今天总算收尾了。这道题的理解让POJ 1733变得容易理解:)
具体代码如下:
#include<cstdio>
const int MAXN = 2e5 + 111;
int p[MAXN], dis[MAXN], ans; // dis[x]代表x到根结点的距离
int find(int x)
{
if (p[x] == x) return x;
int t = p[x];
p[x] = find(p[x]); // 这里的整个处理就是更新dis[x]
dis[x] += dis[t]; // 例如:A->B->C,A本来只指向B,现在更新到C。那么dis[A](A->C) = dis[B](B->C) + dis[A](A->B);
return p[x]; // 每一次压缩路径都会使得dis变成从这个节点,指向其根节点的距离。
}
bool unite(int x, int y, int sum)
{
int rx = find(x), ry = find(y);
if (rx == ry)
{
if (dis[x] - dis[y] == sum)
return 0;
else return 1;
}
else
{
p[rx] = ry;
// dis[rx] = rx -> ry
// dis[y] = y -> ry
// dis[x] = x -> rx
// sum = x -> y
// 显然答案就是下面这个式子了
dis[rx] = dis[y] + sum - dis[x];
return 0;
}
}
int main()
{
int n, m, ans = 0, a, b, sum;
while(~scanf("%d%d", &n, &m))
{
ans = 0;
for (int i = 0; i <= n; ++i) p[i] = i, dis[i] = 0;
for (int i = 0; i < m; ++i)
{
scanf("%d%d%d", &a, &b, &sum);
--a;
if (unite(a, b, sum)) ++ans;
}
printf("%d\n", ans);
}
return 0;
}