ABC 328F Good Set Query

题意
直接看题吧https://atcoder.jp/contests/abc328/tasks/abc328_f

题解
本题主要考了带权并查集,具体实现是在路径压缩的时候顺便维护一下边权(其中w[i]表示点i距离它的祖先的边权之和,fa[i]是点i的祖先)。依次遍历每一次询问,如果询问中的a与b拥有公共祖先,也就是在同一个并查集里。那么直接访问w[a]-w[b]是否等与d即可。如果不在,那么说明在此之前他俩没关系,那么就要合并了,注意:合并是合并两者的祖先,那么俩个祖先之间的边权是多少呢?假如我们要把b的祖先合并到a所属的并查集里去,那么假设这个边为x,那么b这个点到最后的祖先的边权之和为w[b]+x。此时w[a]是不变的,那么w[a]-(w[b]+x)==d 可以很轻松地推导出x=w[a]-w[b]-d,本题结束。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
vector<int>ans;
int fa[maxn],w[maxn];

int find(int x)
{
	if (x!=fa[x])
	{
		int t=fa[x];
		fa[x]=find(fa[x]);
		w[x]+=w[t];
	}
	return fa[x];
}

bool hb(int x,int y,int d)//把y的祖先接给x的祖先 
{
	int fa1=find(x);
	int fa2=find(y);
	if(fa1==fa2)
	{
		if(w[x]-w[y]==d) return 1;
		else return 0;
	}
	else 
	{
		int xx=w[x]-w[y]-d;
		fa[fa2]=fa1;
		w[fa2]+=xx;
		return 1;
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		w[i]=0;
		fa[i]=i;
	}
	for(int i=1;i<=q;i++)
	{
		int a,b,d;
		cin>>a>>b>>d;
		if(hb(a,b,d)) ans.push_back(i);
	}
	for(int i=0;i<ans.size();i++)
	  cout<<ans[i]<<' ';
	
	return 0;
 } 
posted on 2024-06-19 18:23  Linear_L  阅读(8)  评论(0编辑  收藏  举报