http://acm.hdu.edu.cn/showproblem.php?pid=3047
#Description
有一个体育馆,每行300个坐位
来了N个人,提出M组要求
要求格式为 A B X
假设A的坐位是i, 则B的坐位是 i + X
如果来了一组要求和之前矛盾,忽略这组要求
问最终由多少组矛盾的要求
#Algorithm
带权并查集
用权V[i]表示X[id[i]] - X[i]
X[i] 为 第i个人做的位置
查找(find)时,启用路径压缩,则
v[p] = v[p] + v[t] t为之前的id[p]
因为路径压缩之后,id[p] == id[t] 假设 为 root
v[p] = x[root] - x[p]
v[t] = x[root] - x[t]
一开始
v[p] = x[t] - x[p]
所以 v[p] + v[t] = 新的v[p]
合并(unite)时,找的p, q的根i, j
如果 i == j
由于启用了路径压缩
v[p] = x[root] - x[p]
v[q] = x[root] - x[q]
定义 unite传入的参数x为 x[q] - x[p],
v[p] - v[q] = x[q] - x[p] 与 x 比较 不等则表示 这条信息矛盾
如果 i != j
那么就要根进行变化
使用加权并查集树的权值sz
假定sz[i] > sz[j] 即 root = i 的是大树,要将j 并到 i上去
id[j] = i;
sz[i] += sz[j];
关键是v[j]的变化
变化的v[j] = x[i] - x[j]
一开始v[j] = 0 因为v[j]是根
v[p] = x[i] - x[p]
v[q] = x[j] - x[q]
x = x[q] - x[p]
得到
v[j] = v[p] - x - v[q]
同理可得当 j 是大树的情况
#Code
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXL = 50000;
int ans;
int n, m;
int id[MAXL], sz[MAXL], v[MAXL]; //v[i] = x[id[i]] - x[i]
void init(int n)
{
for (int i = 0; i < n; i++) {
id[i] = i;
sz[i] = 1;
v[i] = 0;
}
}
int find(int p)
{
if (p != id[p]) {
int t = id[p];
id[p] = find(id[p]);
v[p] += v[t];
}
return id[p];
}
void unite(int p, int q, int x)
{
int i = find(p), j = find(q);
if (i == j) {
if (v[p] - v[q] != x) {
ans++;
}
return;
}
if (sz[i] > sz[j]) {
id[j] = i;
sz[i] += sz[j];
v[j] = v[p] - x - v[q];
} else
{
id[i] = j;
sz[j] += sz[i];
v[i] = v[q] + x - v[p];
}
}
void solve()
{
ans = 0;
init(n);
while (m--) {
int a, b, x;
scanf("%d%d%d", &a, &b, &x);
a--;
b--;
unite(a, b, x);
}
printf("%d\n", ans);
}
int main()
{
// freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &m) != EOF) {
solve();
}
}