ABC 328F Good Set Query

Linear_L發表於2024-06-19

題意
直接看題吧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;
 } 

相關文章