题解
题目大意,给你若干个区间和,问和之前所说相悖的有多少个,注意多组样例输入
使用带权并查集求解,将区间x, y转化为[x, y)的左闭右开区间方便处理
令f[i]表示i的父节点是谁,要求父节点大于等于子节点,r[i]表示[i, f[i])区间和为多少,初始化时自身父节点为自身,自然[i, i)为0
令f[i]表示i的父节点是谁,r[i]表示区间[i, f[i])的和为多少
压缩路径时直接加上父节点到祖父节点的和,即[x, fx) + [fx, ffx) = [x, ffx),fx表示x的父节点ffx表示x的祖父节点
合并集合时将fx接在fy后面,利用关系传递性fx->fy=fx->x->y->fy,需要注意fx和fy大小关系(好像不判断为负也不影响答案 )
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 2e5 + 10;
int f[MAXN], r[MAXN]; //f[i]表示i的父节点 要求父节点大于等于子节点 r[i]表示区间[i, f[i])的和
int find(int x) //查询x的根节点是谁
{
if (f[x] != x)
{
int fx = f[x]; //记录原父节点
f[x] = find(f[x]); //压缩路径
r[x] = r[x] + r[fx]; //[x, fx) + [fx, ffx) = [x, ffx)
}
return f[x];
}
void join(int x, int y, int z) //设置x与y的[x, y)区间和为z
{
int fx = find(x), fy = find(y); //得到xy的父节点并压缩至直连根节点
if (fx != fy) //不是同一个父节点
{
if (fx > fy) //保证父节点大于等于子节点
swap(fx, fy), swap(x, y), z = -z; //全部翻转
f[fx] = fy; //把fx接在fy上
r[fx] = -r[x] + z + r[y]; //传递fx->x->y->fy
}
}
int query(int x, int y) //查询[x, y)的和 不能确定为-1
{
if (find(x) == find(y)) //同一个根节点才能确定
return r[x] - r[y]; //[x, f) - [y, f) = [x, y)
return -1;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n, m;
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 1; i <= n + 1; i++)
f[i] = i, r[i] = 0; //初始自身为自身父节点 [i, i)为0
int ans = 0;
while (m--)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
y++; //转换为[x, y+1)
int k = query(x, y);
if (k == -1) //无法推断
join(x, y, z);
else if (z != k) //和已有信息不同
ans++;
}
cout << ans << endl;
}
return 0;
}