首页 > 代码库 > hdu3038(带权并查集)

hdu3038(带权并查集)

题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=3038

 

题意: n表示有一个长度为n的数组, 接下来有m行形如x, y, d的输入, 表示从第x,个元素到第y个元素的和为d(包括x, 和y), 问m行输入里面有几个是错误的(第一个输入是正确的);

 

思路: 很显然带权并查集咯,我们可以用距离的概念代替和的概念比较好理解一点,d表示x到y的和即x到y的距离;

可以用rank[x]表示x到其父亲节点的距离,  将正确的距离关系合并到并查集中(错误的当然不能合并到里面啦);

要注意的是这里的x, y是闭区间, 我们不好直接处理, 要先将其变成开区间来, [x, y]等价于(x-1, y]或者[x, y+1);

还有要注意有多组输入(坑爹)......

 

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #define MAXN 200010
 4 using namespace std;
 5 
 6 int pre[MAXN], ran[MAXN];
 7 
 8 int find(int x){
 9     if(x!=pre[x]){
10         int fx=find(pre[x]);
11         ran[x]=ran[x]+ran[pre[x]]; //***路径压缩过程中更新rank的值, 至于这个更新的公式嘛画线段图很容易看出来的啦
12         pre[x]=fx;
13     }
14     return pre[x];
15 }
16 
17 int jion(int x, int y, int d){
18     int fx=find(x);
19     int fy=find(y);
20     if(fx==fy){ // **路径压缩后fx,fy即为x, y的父亲节点, 如果他们父亲节点相同的话我们就可以判断是否与并查集里面的关系矛盾,这个我们也可以画线段图看出来啦
21         if(ran[x]-ran[y]!=d){
22             return 1;
23         }
24     }else{ //***合并后更新rank值, 和前面一样画线段图就OK了啦
25         if(fx<fy){
26             pre[fx]=fy;
27             ran[fx]=ran[y]+d-ran[x];
28         }else{
29             pre[fy]=fx;
30             ran[fy]=ran[x]-ran[y]-d;
31         }
32     }
33     return 0;
34 }
35 
36 int main(void){
37     int n, m, d, x, y, ans=0;
38    while(~ scanf("%d%d", &n, &m)){
39     ans=0;
40     for(int i=0; i<=n; i++){
41         pre[i]=i;
42         ran[i]=0;
43     }
44     while(m--){
45         scanf("%d%d%d", &x, &y, &d);
46         if(jion(x, y+1, d)){ //注意题目给出的是闭区间, 我们将其化成半开区间来比较好处理
47             ans++;
48         }
49     }
50     printf("%d\n", ans);
51     }
52     return 0;
53 }

 

hdu3038(带权并查集)