P2294 [HNOI2005]狡猾的商人 带权并查集

顺便回顾下带权并查集。

在普通并查集的基础上维护一个d数组,表示权值。

这题d[i]表示当前节点x与其祖宗节点get(x), 的sm差值,即sm[x]-sm[get(x)] =d[x]

初始d均为0.

给了一组 l,r, w 。

先令l--.

若l,r,在一个集合里。如下图

P2294 [HNOI2005]狡猾的商人 带权并查集_第1张图片

则sm[r]-sm[l] = d[r]-d[f] -(d[l] - d[f])=d[r]-d[l];

判断这个值是否等于w即可。

若l,r不在一个集合里,要合并这两个集合。

P2294 [HNOI2005]狡猾的商人 带权并查集_第2张图片

如图,我们让l的祖宗fl指向r的祖宗fr。

已知:sm[r]-sm[l]=w;

即:合并后的集合里:d[r]-d[l]-k=sm[r]-s[l]=w

则:k=d[r]-d[l]-w.

令d[fl]=k; 即可。

在路径压缩时,把每个节点的d值更新为其到其祖宗节点的距离即可。(类比树上dfs递归求深度)

#include 
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e3+7;
int fa[M],d[M];
int get(int x)
{
	if(fa[x]==x)return x;
	int rt=get(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=rt;
}

int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int t;
  	cin>>t;
  	while(t--)
  	{
  		int n,m;
  		cin>>n>>m;
  		for(int i=0;i<=n;i++)fa[i]=i,d[i]=0;
  		bool f=true;
  		for(int i=1;i<=m;i++)
  		{
  			int x,y,z;
  			cin>>x>>y>>z;
  			x--;
  			int gx=get(x),gy=get(y);
  			if(gx==gy)
  			{
  				int c=d[y]-d[x];
  				if(c!=z)f=false;
			}
			else
			{
				fa[gx]=gy;
				d[gx]=d[y]-d[x]-z;
			}
		}
		if(f)cout<<"true"<

 

你可能感兴趣的:(#,数据结构----并查集)