How Many Answers Are Wrong (带权并查集模板)

How Many Answers Are Wrong

题目描述 :

TT和FF是…朋友。呃…非常非常好的朋友

FF是个坏孩子,他总是向TT示好,让他和他一起玩下面这个游戏。这是一个非常乏味的游戏。首先,TT应该写下一个整数序列-_-!!(无聊)。

然后,FF可以从中选择一个连续的子序列(例如,从第三个到第五个整数的子序列,包括在内)。之后,FF会问TT他选择的子序列的总和是多少。接下来,TT会回答FF的问题。然后,FF可以重做这个过程。最后,FF必须算出整个整数序列。

无聊无聊一个非常非常无聊的游戏!! TT根本就不想和FF玩。为了惩罚FF,她经常故意告诉FF错误的答案。

这个坏小子不是个傻子。FF检测到一些答案是不相容的。当然,这些矛盾让我们很难计算出序列。

然而,TT是一个善良可爱的女孩。她没有心思去为难FF。为了节省时间,她保证,如果确实没有逻辑错误,答案都是正确的。

更重要的是,如果FF发现一个答案是错的,他在判断下一个答案时就会忽略它。

但是问题太多,可怜的FF无法在短时间内确定当前的答案是对还是错。所以他决定写一个程序来帮助他解决这个问题。这个程序将接收来自FF的一系列问题,以及FF从TT那里得到的答案。这个程序的目的是找出有多少答案是错误的。只有通过忽略错误的答案,FF才能算出整个整数序列。可怜的FF没有时间来做这项工作。现在他在请求你的帮助(为什么要给自己找麻烦~坏孩子)

输入
第1行。两个整数,N和M(1<=N<=200000,1<=M<=40000)。意味着TT写了N个整数,FF问了她M个问题。

第2行…M+1。第i+1行包含三个整数。Ai,Bi和Si。意味着TT回答FF,从Ai到Bi的总和是Si。可以保证0 < Ai <= Bi <= N。

你可以假设任何子序列的和都适合于32位整数。

输出
一行整数表示有多少个答案是错误的。

输入样本

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

样本输出

1

思路解析

题目大意 :
给出一个长度为n的序列,然后给出m对查询,需要你找出自相矛盾的个数

并查集维护权值的正确性
带权并查集模板

int pre[maxn];
int value[maxn];    // value是该节点到其根的和,比如说value[3],3的根是1,那么value[3]表示的就是1到3的和……
void init(){
    for(int i = 0; i <= n; i++) {
            pre[i] = i;
            value[i] = 0;
    }
}
int find(int x) {
    if (x == pre[x])  return x;
    else {
        int root = find(pre[x]);     // 找到根节点
        value[x] += value[pre[x]];   // 权值合并,更新
        return pre[x] = root;        // 压缩路径
    }
}
//带权路径的合并
void merge(){
        int px = find(x);
        int py = find(y);
        if (px != py)
        {
            pre[px] = py;
            value[px] = -value[x] + value[y] +s;
        }//s是他们之间的关系
}

AC代码

#include <cstdio>
const int maxn = 200000 + 10;
int n, m;
int pre[maxn];
int value[maxn];    // value是该节点到其根的和,比如说value[3],3的根是1,那么value[3]表示的就是1到3的和……
void init(){
    for(int i = 0; i <= n; i++) {
            pre[i] = i;
            value[i] = 0;
    }
}
int find(int x) {
    if (x == pre[x])  return x;
    else {
        int root = find(pre[x]);     // 找到根节点
        value[x] += value[pre[x]];   // 权值合并,更新
        return pre[x] = root;        // 压缩路径
    }
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        init();
        int x, y, s;
        int ans = 0;
        while(m--) {
            scanf("%d%d%d", &x, &y, &s);
            x--;        // 想一下为什么要减一,可以让类似【1,5】【6,10】这样的区间可以合并……
            int fx = find(x);
            int fy = find(y);
            if (fx != fy) {
                pre[fy] = fx;
                value[fy] = value[x] - value[y] + s;
            }
            else if (value[y] - value[x] != s)  ans++;//此时value[y]的值就等于到根节点的距离,应该等于value[x]+s;
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值