題目鏈接: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; }