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;
}