题目链接:[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; }