How Many Answers Are Wrong (HDU - 3038)(带权并查集)

题目链接

并查集是用来对集合合并查询的一种数据结构,或者判断是不是一个集合,本题是给你一系列区间和,判断给出的区间中有几个是不合法的。 

思考:
1.如何建立区间之间的联系
2.如何发现悖论
首先是如何建立联系,我们可以用一张图表示

假如说区间【fx,x】是之前建立的区间,他们之间和为sum[x],fx和x的联系可以用集合来存储,同理【fy,y】也是如此。当给出了一个新的区间【x,y】时,且区间和为s。就产生了两种情况了,如果fx == fy 那么这两个区间是有关联的区间,也就是【x,y】之间的和是可以求出的。可以把这个图看成一个向量。 区间【x,y】的和就是可以写成sum[x] - sum[y]。判断给出的s与向量法计算的区间和是否相等就可以判断是否是悖论。
当然如果fx != fy就需要建议新的区间关系。首先将fy指向fx,这代表fx是区间的左端点,计算sum【fy】= sum【x】- sum【y】+ s;这里同样用的是向量法。
这样建立联系与判断悖论都可以表达了,接下来就是一些细节了,比如在更新区间的时候要进行路径的压缩,压缩的过程中需要对权值进行更新,目的是使每个已知区间最大化。

解题思路:这个题乍一看可能要线段树或树状数组,其实没有必要,区间和可以理解为前缀和相减。每个节点记录前缀和,对每个询问先判断两个节点是否连通,这便是带权并查集干的事了,若联通则权值相减看是否为给定值,若不为则矛盾;若不联通则两棵树合并为一棵(注意有顺序)同时计算出新的子节点的权值。

AC代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 200000 + 7;

int pre[maxn] ,sum[maxn];//sum是该节点到其根的和,如sum【3】
//,3的根是1,就是1到3的和

int find(int x){
    if(x == pre[x]) return x;
    else {

        int root = find(pre[x]);
        sum[x] += sum[pre[x]];
        return pre[x] = root;
    }
}


void init(int n){
    for(int i = 0 ; i <= n;i++){
        pre[i] = i;
        sum[i] = 0;
    }
}
int ans = 0;
void unions(int x,int y,int s){
    int fx = find(x);
    int fy = find(y);
    if(fx != fy){
        pre[fy] = fx;
        sum[fy] = sum[x] - sum[y] + s;
    }
    else if(sum[y] - sum[x] != s) 
        ans ++;

}
int main(){
int n,m;
//cin >> n >> m;
while(cin >> n >> m){
    init(n);
    int x,y;
    int s;
    ans = 0;
    while(m--){
        cin >> x >> y >> s;
        x --;
        unions(x,y,s);
    }

    cout << ans << endl;
}


return 0;
}

猜你喜欢

转载自www.cnblogs.com/DWVictor/p/10510002.html