ABC328F题解
闲话
赛场就做了这道和 A,喜提
带权并查集练手题,有点像银河英雄传说。
题目大意
存在一个长度为
的数列 ,给定 个三元组 ,定义一个好集合为集合 ,使得所有 满足 。
初始为空集,以从 到 执行以下操作: 设当前操作的数为
,如果 为好集合,则以 代替 。 以升序打印出
中的元素。
题目分析
既然题目让我们从
对于这个操作我们只需判断是否与以前的操作构成数列的相对关系矛盾即可。
如果没有规定
如果规定过
这个关系是逐步添加却不会消失的,也会有传递性。
于是我就想到了并查集。
我们还要维护这两个值的差,于是我们可以使用带权并查集。
对于有关系的数就是合并在一个集合里,我们维护父节点与每个节点的差(也可维护相反数,本质相同)即可。
没有关系就不在一个集合里,进行合并操作。
有关系我们就判断差是否相等,相等就输出即可,因为这个关系的添加与否是等效的(不添加原来的操作也能判断关系),所以我们不进行合并操作也可以。
使用路径压缩的查询
设
如下图,
查询时该节点
合并
我们只需考虑不同集合的合并(因为相同的没必要合并)。
这里合并使
我们需要满足
由上式和
对其赋值即可。
代码中的 check
是来检测是否可以添加
代码如下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=2e5+10;
int a,b,d,fa[MAXN],N,Q;
ll dis[MAXN];
int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return f*x;
}
int sget(int x){
if(fa[x]==x)return x;
int root=sget(fa[x]);
dis[x]+=dis[fa[x]];
return fa[x]=root;
}
void merge(int x,int y,int w){
int fx=sget(x),fy=sget(y);
if(fx!=fy){
fa[fx]=fy;
dis[fx]=w+dis[y]-dis[x];
//X_x-X_y=w
//dis[x]+dis[fx]-(dis[y]+dis[fy])=w
}
}
bool check(){
int faa=sget(a),fab=sget(b);
if(faa!=fab||dis[b]+d==dis[a])return true;
else return false;
}
int main(){
N=read();Q=read();
for(int i=1;i<=N;++i){
fa[i]=i;
}
for(int i=1;i<=Q;++i){
a=read();b=read();d=read();
if(check()){
merge(a,b,d);
printf("%d ",i);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】