HDU_3038 How Many Answers Are Wrong 【带权并查集】
一、题面
二、分析
用并查集可以方便的判断两个位置是否有关系,这种关系可以通过是否有公共父节点判断,如果有公共父节点则可以直接判断是否正确,如果没有公共父节点,就可以把这个条件与之前的联系起来。同时需要设定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 }