1. 程式人生 > >How Many Answers Are Wrong 【HDU

How Many Answers Are Wrong 【HDU

題目連結

 思路

  題目讓我們求的是一共N個數的Q次詢問,每次詢問判斷是否是個正確解,如果是個正確,或者不能確定的解,那麼我們就把它當作條件存起來,反之,如果確定它是個錯解,我們就對於最後要輸出的答案+1。

  那麼,問題就是怎麼處理錯誤與需要判斷的條件了,這道題一開始的不理解在於如果一個區間確定,但是現在詢問它的一個真子集區間,且真子集空間比父區間大怎麼辦?沒有關係,題目並沒有說明區間一定是個正數,所以可以負數,那麼這道題就可以解了。

 這就是一個並查集的思維了,我們將一個區間【a,b】看作並查集的兩個需要連結的點,但是這樣會發現一件事,就是用到種類並查集的時候,我們求從根節點到目前節點的字首和
,值就會少去a點的值,所以,我處理的是【a-1,b】

  接下來,就是最繁瑣的連結問題,我令a->b為a節點到b節點的距離,舉個例子,令u==root(a),那麼,sum[a]就是a到其根節點的距離,那麼對於同一棵樹上的節點,我欲求a->b的距離,怎麼求?欲求a->b,已知root(a)->a、root(b)->b,即sum[a],sum[b],又因為它們在同一棵樹上root(a) == root(b),看作root,向量法來看,就是a->b = -root(a)->a + root(b)->b,也就是說,a到b的距離為sum[b]-sum[a]

  既然知道了,同一棵樹上該如何處理,我們用同樣的方法處理一下不同樹的情況。已知a、b它們在不同的樹上,已知root(a)->a、root(b)->b,我們想將a的根連在b的根上,即所求解為:root(b)->root(a)root(b)->root(a) == root(b)->b + b->a - root(a)->a;即sum[root(a)] = sum[b] + det - sum[a]

完整程式碼

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxN=200005;
int N, M, ans=0;
int root[maxN];
int sum[maxN];
int fid(int x)
{
    if(x==root[x]) return x;
    int tmp=root[x];
    root[x]=fid(root[x]);
    sum[x]+=sum[tmp];
    return root[x];
}
void mix(int x, int y, int det)     //對於位於前面的數需要“-1”,不然則要特判x點上的值
{
    int u=fid(x), v=fid(y);
    if(u==v)
    {
        if(sum[x] - sum[y] != det) ans++;
    }
    else
    {
        root[u]=v;     
        sum[u]=det+sum[y]-sum[x];
    }
}
int main()
{
    while(scanf("%d%d", &N, &M)!=EOF)
    {
        ans=0; for(int i=0; i<=N; i++) { root[i]=i; sum[i]=0; }
        for(int i=1; i<=M; i++)
        {
            int e1, e2, e3;
            scanf("%d%d%d", &e1, &e2, &e3);
            mix(e1-1, e2, e3);
        }
        printf("%d\n", ans);
    }
    return 0;
}