1. 程式人生 > >HDU_3038 How Many Answers Are Wrong 【帶權並查集】

HDU_3038 How Many Answers Are Wrong 【帶權並查集】

一、題面

HDU3038

二、分析

用並查集可以方便的判斷兩個位置是否有關係,這種關係可以通過是否有公共父節點判斷,如果有公共父節點則可以直接判斷是否正確,如果沒有公共父節點,就可以把這個條件與之前的聯絡起來。同時需要設定sum,表示當前點到父節點的權值,這個權值方便後面的判斷,這裡有幾種情況。

假設輸入為$(x,y,w)$,那麼對應父節點$fx = find(x), fy = find(y)$:

1.如果$fx==fy$:那麼可以直接判斷$w == sum[x] - sum[y]$.

2.如果$ x < y < fx < fy$:這裡需要更新並查集的資訊,$par[fx] = fy, sum[fx] = sum[y] - (sum[x] - w)$.

3.如果$ x < fx < y < fy$:此時,$par[fx] = fy, sum[fx] = sum[y] + (w - sum[x])$.

4.如果$ x < y < fy < fx$:此時,$par[fy] = fx, sum[fy] = sum[x] - sum[y] - w$.

分析上面四種情況,可以得出2和3可以合併,對於4,仔細分析一下, 相當於就是3的情況,只是因為$par[fy] = fx$,只是這樣可以保證$sum$的值為正。注意在路徑壓縮時,對$sum$進行更新就可以了。

細心的朋友可能發現了,還有$x = y$的情況呢。對於這種情況,我們可以直接對輸入的左值整體再往左移一下,就是輸入的$u$變成$u-1$,這個對結果是沒有影響的。但是如果不減,我們就要考慮$x=y$的情況了,前面我們已經初始化了$sum$初值為0,並且$x=y$的左右兩邊的情況我們是不能保證完全清楚的。所以這樣把$x=y$變成了$(x-1,y]$這個區間的形式,就讓問題又歸到了我們上面討論的情況,更簡單了。

三、AC程式碼

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <fstream>
 5 #include <map>
 6 
 7 using namespace std;
 8 
 9 const int MAXN = 2e5+3;
10 int par[MAXN];
11 int sum[MAXN];
12 
13 void Init()
14 {
15     for(int i = 0; i < MAXN; i++)
16 par[i] = i; 17 memset(sum, 0, sizeof(sum)); 18 } 19 20 int Find(int x) 21 { 22 if(par[x] == x) 23 return x; 24 int fx = par[x]; 25 par[x] = Find(fx); 26 sum[x] = sum[fx] + sum[x]; 27 return par[x]; 28 } 29 30 bool Union(int x, int y, int w) 31 { 32 int fx = Find(x); 33 int fy = Find(y); 34 // if(fx != fy) 35 // { 36 // par[fx] = fy; 37 // sum[fx] = w + sum[y] - sum[x]; 38 // return true; 39 // } 40 // else 41 // { 42 // return w == sum[x] - sum[y]; 43 // } 44 if(fx < fy) 45 { 46 par[fx] = fy; 47 sum[fx] = w + sum[y] - sum[x]; 48 return true; 49 } 50 else if(fx > fy) 51 { 52 par[fy] = fx; 53 sum[fy] = sum[x] - w - sum[y]; 54 return true; 55 } 56 else 57 { 58 return w == sum[x] - sum[y]; 59 } 60 61 } 62 63 int main() 64 { 65 // freopen("input.txt", "r", stdin); 66 int N, M; 67 while(scanf("%d %d", &N, &M)!=EOF) 68 { 69 Init(); 70 int a, b, w, ans = 0; 71 for(int i = 0; i < M; i++) 72 { 73 scanf("%d %d %d", &a, &b, &w); 74 if(!Union(a-1, b, w)) 75 ans++; 76 } 77 printf("%d\n", ans); 78 } 79 return 0; 80 }
View Code