[Problem Description]
刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3...n-1,n)。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。 刁姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。 现在,刁姹总共偷看了m次账本,当然也就记住了m段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。
[Algorithm]
并查集
[Analysis]
并查集用的非常巧妙。我们把能够连在一起的点放到一个并查集里面。并查集维护两个值:Father[i]和Sum[i]。Sum[i]表示从i节点到Father[i]的收入。如果读入的区间两端点在同一个并查集里面,就判断一下Sum[t] - Sum[s]是否为输入的值。如果两个端点不在一个并查集里面,就把t所在的并查集合并到s里面。Sum[Father[s]] = Sum[t] + value - Sum[s]
[Pay Attention]
注意端点。可以将输入的t加上1,好处理
[Code]
/**************************************************************
Problem: 1202
User: gaotianyu1350
Language: C++
Result: Accepted
Time:208 ms
Memory:1272 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 120;
const int MAXM = 1100;
int father[MAXN], sum[MAXN];
int n, m, testcase;
inline void clear(int n)
{
for (int i = 1; i <= n + 1; i++)
father[i] = i, sum[i] = 0;
}
inline int getfather(int x)
{
if (father[x] == x)
return x;
int oldfather = father[x];
father[x] = getfather(father[x]);
sum[x] += sum[oldfather];
return father[x];
}
int main()
{
scanf("%d", &testcase);
while (testcase--)
{
scanf("%d%d", &n, &m);
clear(n);
bool isok = true;
for (int i = 1; i <= m; i++)
{
int s, t, v;
scanf("%d%d%d", &s, &t, &v);
t++;
int fs = getfather(s);
int ft = getfather(t);
if (fs == ft)
{
if (sum[t] - sum[s] != v)
{
isok = false;
break;
}
}
else
{
father[ft] = fs;
sum[ft] = sum[s] + v - sum[t];
}
}
if (isok) printf("true\n");
else printf("false\n");
}
}