使用带权并查集可以解决。
在维护并查集关系信息的同时,需要维护一个数组disdisdis。
dis[i]dis[i]dis[i]代表了从iii到当前族的根节点的距离。
问题1 如何维护dis信息
考虑如果有lll和rrr两个节点,如果他们的根节点flflfl和frfrfr并不是同一个,那么考虑合并。
我们将fa[fl]=frfa[fl] = frfa[fl]=fr,也就是将lll连接到rrr,这个时候我们需要维护dis[fl]dis[fl]dis[fl]的信息,也就是fl→frfl \rightarrow frfl→fr的距离信息fl−frfl - frfl−fr。
已知信息为r−l=dr - l = dr−l=d,dis[r]=r−frdis[r] = r - frdis[r]=r−fr,dis[l]=l−fldis[l] = l - fldis[l]=l−fl。
那么dis[r]−dis[l]=fl−fr+r−ldis[r] - dis[l] = fl - fr + r - ldis[r]−dis[l]=fl−fr+r−l。
这个时候移项可得:dis[r]−dis[l]−(r−l)=fl−frdis[r] - dis[l] - (r - l) = fl - frdis[r]−dis[l]−(r−l)=fl−fr。
也就是fl−fr=dis[r]−dis[l]−dfl - fr = dis[r] - dis[l] - dfl−fr=dis[r]−dis[l]−d。得证。
问题2 如何查询
当两个节点为同一个组的时候,需要判断r−l==dr - l == dr−l==d。
根据问题1的证明方式,易得:dis[r]−dis[l]==r−ldis[r] - dis[l] == r - ldis[r]−dis[l]==r−l。
code:
#include <bits/stdc++.h>
using namespace std;
#define close ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define endl '\n'
int fa[MAXN],dis[MAXN];
//类似于后序遍历,一直维护当前节点到根节点的距离即可。
int find(int x){
if(x != fa[x]){
int t = fa[x];
fa[x] = find(t);
dis[x] += dis[t];
}
return fa[x];
}
signed main(){
close
int n,m; cin >> n >> m;
for(int i = 1; i <= n; i++){
fa[i] = i;
dis[i] = 0;
}
bool ok = true;
while(m--){
int l,r,d; cin >> l >> r >> d;
int fl = find(l),fr = find(r);
if(fl == fr && dis[r] - dis[l] != d){
ok = false;
}
if(fl != fr){
// fl -> fr
fa[fl] = fr;
// dis[r] 代表了 r到fr的距离
// dis[l] 代表了 l到lf的距离
dis[fl] = dis[r] - dis[l] - d;
}
}
if(ok) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
242

被折叠的 条评论
为什么被折叠?



