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

时间:2023-03-09 01:05:09
How Many Answers Are Wrong (HDU - 3038)(带权并查集)

题目链接

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

思考:
1.如何建立区间之间的联系
2.如何发现悖论
首先是如何建立联系,我们可以用一张图表示
How Many Answers Are Wrong (HDU - 3038)(带权并查集)
假如说区间【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 = + ; 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 = ; i <= n;i++){
pre[i] = i;
sum[i] = ;
}
}
int ans = ;
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 = ;
while(m--){
cin >> x >> y >> s;
x --;
unions(x,y,s);
} cout << ans << endl;
} return ;
}