hdu 3038(擴展並查集)


題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3038

 

題意:給出區間[1,n],下面有m組數據,l r v區間[l,r]之和為v,每輸入一組數據,判斷此組條件是否與前面沖突 ,最后輸出與前面沖突的數據的個數.
比如 [1 5]區間和為100 然后后面給出區間[1,2]的和為 200 那肯定就是有問題的了。

 

極力推薦這位大牛的博客:http://blog.csdn.net/niushuai666/article/details/6981689

這題讓我學到了很多,特別是關於向量偏移,可以直接找到根節點與子節點的關系

 

這題我們利用一個sum[]數組保存從某點到其祖先節點距離

1.從上圖我們可以看出,當roota!=rootb時 如果將roota並入rootb,那么是不是 roota->rootb = b->rootb - b->roota

然后我們可以知道 b->roota = a->roota - a->b

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

而roota的根節點是rootb,所以 roota->rootb = sum[roota]

然后依次推出得到 sum[roota] = -sum[a]+sum[b]+v (這里的a要說明一下由於是區間 [a,b] ,[a,b] = [root,b]-[root,a-1],所以a要減一)

2.如果roota==rootb 是不是 a和b的根節點已經相同了?所以我們只要驗證 a->b是否與題中的長度一致了。

所以 a->b = a->root - b->root

然后得到表達式 v = sum[a]-sum[b] (一定要記住這里的sum都是相對於根節點的,sum的更新在路徑壓縮的時候更新了)

這樣說是不是懂了向量偏移的思想呢??

下面上代碼:

#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N =200010;
int father[N];
int sum[N];  ///記錄當前結點到根結點的距離

int _find(int x){
    if(x!=father[x]){
        int t = father[x];
        father[x] = _find(father[x]);
        sum[x]+=sum[t];
    }
    return father[x];
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        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 = _find(a);
            int rootb = _find(b);
            if(roota==rootb){
                if(sum[a]-sum[b]!=v) ans++;  ///精華部分1
            }
            else{
                father[roota] = rootb;
                sum[roota] = -sum[a]+sum[b]+v; ///精華部分2
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM