[BZOJ1202][HNOI2005][并查集]狡猾的商人

[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");
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值