The Third Letter

这是一个并查集的题目。

题目要求我们维护多个人之间关系的传递(譬如说已知 A 在 B 前面 ,B 在 C 前面 ,则应该能计算出 前面 )。

考虑并查集:已知 处于一个集合, 处于一个集合,则 处于一个集合。这是不是很像题目要求我们维护的关系?因此。我想到使用并查集解决这个问题。

题目要维护人们之间的距离,因此原先的 fa 数组应当作修改。fa 数组的定义如下:

struct node{size_t p;long long int dis;};
vector<node> fa;

其中,p 仍然表示父节点,而 dis 表示子结点在父节点前面的距离(若在后面则为负数)。

find 函数除了要找到树根,还要计算当前节点在树根前面的距离

node find(size_t x){
    if (x==fa[x].p) return {x,0}; //自己就是树根,与树根的距离就是0
    else{
        node res=find(fa[x].p);
        return fa[x]={res.p,res.dis+fa[x].dis}; 
        /*树根就是 res.p;
          当前节点到树根的距离 = 父节点与根的距离 res.dis + 当前节点与父节点的距离 fa[x].dis。
          同时路径压缩,fa[x] 接记录与根的距离。*/
    }
}

最后,就是最重要的合并函数了。我选择将查询函数与合并函数放到一块写。若 xy 不在一个集合内(关系无法判断),则合并然后返回 true;否则,根据并查集维护的两点之间的距离与给定条件比对。

bool merge(size_t x,size_t y,long long int dis){
    node fax=find(x),fay=find(y);
    if (fax.p!=fay.p){
        fa[fax.p]={fay.p,-fax.dis+fay.dis+dis};
        return true;
    }else return fax.dis==fay.dis+dis;
}

最后,不开 long long 见祖宗(某人因此被 hack 了)

完整代码:

#include<bits/extc++.h>
using namespace std;
namespace pbds=__gnu_pbds;
using ui=unsigned int;
class dsds{
    struct node{size_t p;long long int dis;};
    vector<node> fa;
    node find(size_t x){
        if (x==fa[x].p) return {x,0};
        else{
            node res=find(fa[x].p);
            return fa[x]={res.p,res.dis+fa[x].dis};
        }
    }
public:
	//构造函数,将数组大小初始化为 n ,循环赋值父节点为自己,dis 初始化为 0
    dsds(size_t n):fa(n){for (size_t i=0;i<n;i++) fa[i].p=i,fa[i].dis=0;}
    bool merge(size_t x,size_t y,long long int dis){
        node fax=find(x),fay=find(y);
        if (fax.p!=fay.p){
            fa[fax.p]={fay.p,-fax.dis+fay.dis+dis};
            return true;
        }else return fax.dis==fay.dis+dis;
    }
};
int main(void){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    size_t T;
    cin>>T;
    while (T--){
        size_t n,m;bool flag=true;  //是否全部符合的标记
        cin>>n>>m;
        dsds ds(n);
        while (m--){
            size_t a,b;long long int d;
            cin>>a>>b>>d;
            if (!ds.merge(--a,--b,d)) flag=false; //个人习惯从 0 存储因此 -1;当不符合(返回 false)时才标记 flag,因此要作非运算
        }
        cout<<(flag?"YES":"NO")<<'\n';
    }
    return 0;
}
posted @ 2023-07-23 15:23  MrPython  阅读(5)  评论(0)    收藏  举报  来源