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