AT_arc090_b 题解

使用带权并查集可以解决。

在维护并查集关系信息的同时,需要维护一个数组disdisdis
dis[i]dis[i]dis[i]代表了从iii到当前族的根节点的距离。

问题1 如何维护dis信息

考虑如果有lllrrr两个节点,如果他们的根节点flflflfrfrfr并不是同一个,那么考虑合并。

我们将fa[fl]=frfa[fl] = frfa[fl]=fr,也就是将lll连接到rrr,这个时候我们需要维护dis[fl]dis[fl]dis[fl]的信息,也就是fl→frfl \rightarrow frflfr的距离信息fl−frfl - frflfr

已知信息为r−l=dr - l = drl=d,dis[r]=r−frdis[r] = r - frdis[r]=rfr,dis[l]=l−fldis[l] = l - fldis[l]=lfl
那么dis[r]−dis[l]=fl−fr+r−ldis[r] - dis[l] = fl - fr + r - ldis[r]dis[l]=flfr+rl

这个时候移项可得:dis[r]−dis[l]−(r−l)=fl−frdis[r] - dis[l] - (r - l) = fl - frdis[r]dis[l](rl)=flfr

也就是fl−fr=dis[r]−dis[l]−dfl - fr = dis[r] - dis[l] - dflfr=dis[r]dis[l]d。得证。

问题2 如何查询

当两个节点为同一个组的时候,需要判断r−l==dr - l == drl==d
根据问题1的证明方式,易得:dis[r]−dis[l]==r−ldis[r] - dis[l] == r - ldis[r]dis[l]==rl

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值