HDU3038 How Many Answers Are Wrong

How Many Answers Are Wrong

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13057    Accepted Submission(s): 4638


Problem Description
TT and FF are ... friends. Uh... very very good friends -________-b

FF is a bad boy, he is always wooing TT to play the following game with him. This is a very humdrum game. To begin with, TT should write down a sequence of integers-_-!!(bored).

Then, FF can choose a continuous subsequence from it(for example the subsequence from the third to the fifth integer inclusively). After that, FF will ask TT what the sum of the subsequence he chose is. The next, TT will answer FF's question. Then, FF can redo this process. In the end, FF must work out the entire sequence of integers.

Boring~~Boring~~a very very boring game!!! TT doesn't want to play with FF at all. To punish FF, she often tells FF the wrong answers on purpose.

The bad boy is not a fool man. FF detects some answers are incompatible. Of course, these contradictions make it difficult to calculate the sequence.

However, TT is a nice and lovely girl. She doesn't have the heart to be hard on FF. To save time, she guarantees that the answers are all right if there is no logical mistakes indeed.

What's more, if FF finds an answer to be wrong, he will ignore it when judging next answers.

But there will be so many questions that poor FF can't make sure whether the current answer is right or wrong in a moment. So he decides to write a program to help him with this matter. The program will receive a series of questions from FF together with the answers FF has received from TT. The aim of this program is to find how many answers are wrong. Only by ignoring the wrong answers can FF work out the entire sequence of integers. Poor FF has no time to do this job. And now he is asking for your help~(Why asking trouble for himself~~Bad boy)
 

Input
Line 1: Two integers, N and M (1 <= N <= 200000, 1 <= M <= 40000). Means TT wrote N integers and FF asked her M questions.

Line 2..M+1: Line i+1 contains three integer: Ai, Bi and Si. Means TT answered FF that the sum from Ai to Bi is Si. It's guaranteed that 0 < Ai <= Bi <= N.

You can assume that any sum of subsequence is fit in 32-bit integer.
 

Output
A single line with a integer denotes how many answers are wrong.
 

Sample Input
 
  
10 51 10 1007 10 281 3 324 6 416 6 1
 

Sample Output
 
  
1
 

Source

2009 Multi-University Training Contest 13 - Host by HIT


题意:有n个数的一个序列,给你m个query,每个query由a,b,s三个数组成,表示在这个序列中第a个数到第b个数的区间和为s(下标从1开始)

在这之中会有错的序列(即与已经得到出的消息不符的消息认为是错的)

问你在这之中共有几条错误的消息。处理到文件结尾。


那么这道题如果不是放在并查集专题里面不会想到是并查集做。

事实上这类题目的做法叫“带权并查集” 即在并查集过程中用一个数组保存一些与结点相关的信息。


那么对于这道题目。其实是用一个sum数组保存当前结点到根结点的区间和。


扫描二维码关注公众号,回复: 2706619 查看本文章

那么我们对于每个query怎么处理呢。

对于每一个query,首先查询结点的根结点是谁,如果两个的根结点一样,那么我们就有能力判断当前的信息是否正确

因为我们已经保存了结点到根结点的区间和的信息。


如果两个的父亲结点是不同的,那么我们就需要进行合并;

合并的时候按照从下标小的结点向下标大的结点方向进行。

传入的参数为两个元素的根,和两个下标和v值


对于并查集的两个操作Find和Union,下面具体讲解。

对于Find,我们除了要进行路径压缩之外,还需要更新结点的sum值(因为它的根结点发生了变化)

比如说有一条链2 -> 6 -> 8

一开始保存的是2到6的区间和,那么压缩完2直接连一条边到8,所以需要2原先的sum值再加上6的sum值

即:sum[2] = sum[6] + sum[2]

那么这里就需要一个递归先保存当前处理到结点的par值,再找par的根,当递归回来时再加上原先保存的par值的sum值

需要保存的原因是当路径压缩完回来时原先par值已经发生变化了。

可能有点绕,这里画一个图会清楚一点。


那么对于Union操作,可能更绕一点

如果a的根x小于b的根y

那么sum[x] = sum[b] + v - sum[a]

如果a的根y大于b的根

那么sum[y] = sum[a] - sum[b] - v

啊这里好难理解。exemplify it


比如6 -> 10 8 - > 15

那么我们需要从10向15连一条边

所以我们需要计算10->15的区间和即:11,12,13,14,15

所以我们需要用sum[8](9~15) + v(7~8) - sum[6](7~10)


那么如果是8 -> 10 6 -> 15

需要计算的10->15的区间和为sum[6] - v - sum[8]



下面上代码(代码其实还挺简单的,关键是思维)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 20;
int par[maxn];
int sum[maxn];
int n, m;
void init()
{
    for(int i = 0; i <= n; i++)
        par[i] = i;
    memset(sum, 0, sizeof(sum));
}
int _find(int i)
{
    if(i == par[i])
        return i;
    int temp = par[i];
    par[i] = _find(par[i]);
    sum[i] += sum[temp];
    return par[i];
    
}
void Union(int x, int y, int a, int b, int v)
{
    if(x < y)
    {
        par[x] = y;
        sum[x] = v + sum[b] - sum[a];
    }
    else
    {
        par[y] = x;
        sum[y] = sum[a] - v - sum[b];
    }
}
int main()
{
    while(cin >> n >> m)
    {
        init();
        int cnt = 0;
        while(m--)
        {
            int a, b, v;
            cin >> a >> b >> v;
            a--;
            int x = _find(a);
            int y = _find(b);
            if(x == y && sum[a] - sum[b] != v)
                cnt++;
            else Union(x, y, a, b, v);
        }
        cout << cnt <<endl;
    }
    return 0;
}

因为我们需要通过相减的方式得到区间和,所以我们统一对较小的那个下标减一。

同时我们保存的sum值也是一个左开右闭的区间。

以上

猜你喜欢

转载自blog.csdn.net/sinat_37158899/article/details/80471913