HDU - 3038 How Many Answers Are Wrong(带权并查集维护距离)

题目链接https://vjudge.net/contest/320541#problem/C
Sample Input

10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1

Sample Output

1

翻译:n(n=10)个数组成的序列,(不知道这个序列的内容),q(q=5)次询问,每次三个数,表示 [s,t]区间的和。随着输入,若出现与上面的区间和矛盾,就记录矛盾的次数。
比如[1,10]和为100,[1,4]的和为40,[5,10]的和为30,这三组数据中肯定有一组数据是错误的。

分析
先对输入区间的处理,把[u,v]变成(u-1,v],把区间连在一起。
开一个数组sum,记录某点到其祖先节点的距离。对于每一个范围,用并查集把端点合并到一个集合。对于接下来的范围,如果端点在同一集合,可以直接用两点到祖先节点的差值与给点的区间长度比较,不在同一集合,合并,找出根节点与节点的距离关系。
在这里插入图片描述
从上图我们可以看出,当roota!=rootb时 如果将roota并入rootb,那么是不是
roota->rootb = b->rootb - b->roota

我们可以知道 b->roota = a->roota - a->b

最后可以推出 roota ->rootb = b->rootb + a->b - a->roota

因为,sum[roota]= roota->rootb
推出得到 sum[roota] = -sum[a]+sum[b]+v

以下面这组数据模拟过程:

10 3
1 10 100
1 4 40
5 10 60

0

10 3
1 10 100

roota–>0,rootb—>10,father[0]—>10
(0,10的根节点不同,把0的祖先看成是10)
sum[0]—>100(0到祖先10的距离为100)
1 4 40

roota–>10,rootb—>4,father[10]—>4
(0,4的根节点不同,把10的祖先看成是4)
sum[10]—>-60(10到祖先的距离为-60)
5 10 60
roota–>4,rootb—>4(祖先相同可以直接比较)
sum[5]–>0,sum[10]–>-60
0
代码:

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define N 200010
int father[N];
int sum[N];  ///记录当前结点到根结点的距离
int getf(int x)
{
    if(x!=father[x])
    {
        int t=father[x];
        father[x]=getf(father[x]);
        sum[x]+=sum[t];
    }
    return father[x];
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<=n; i++)
        {
            father[i] = i;
            sum[i] = 0;
        }
        int ans = 0;
        while(m--)
        {
            int a,b,v;
            scanf("%d%d%d",&a,&b,&v);
            a--;
            int roota = getf(a);
            int rootb = getf(b);
            if(roota==rootb)
            {
                //printf("sum[a]-->%d,sum[b]-->%d\n",sum[a],sum[b]);
                if(sum[a]-sum[b]!=v)
                    ans++;
            }
            else
            {
                father[roota] = rootb;
                //printf("roota-->%d,rootb--->%d,father[%d]--->%d\n",roota,rootb,roota,father[roota]);
                sum[roota] = -sum[a]+sum[b]+v;
                //printf("sum[%d]--->%d\n",roota,sum[roota]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值