HDU3047-Zjnu Stadium(加权并查集)

先说一下加权并查集:
每个节点都记录的是与自己的根节点之间的权值,
那么在Find的路径压缩过程中,权值也应该做相应的更新,因为在路径压缩之前,每个节点都是与其父节点链接着,每个节点的Value自然也是与其父节点之间的权值。
2.在两个并查集做合并的时候,权值也要做相应的更新,因为两个并查集的根节点不同。
如何更新权值?如何合并?看下面代码:

路径压缩
int find(int x)
{
	if (x != parent[x])
	{
		int t = parent[x];
		parent[x] = find(parent[x]);
		value[x] += value[t];
	}
	return parent[x];
}
合并
	int px = find(x);
	int py = find(y);
	if (px != py)
	{
		parent[px] = py;
		value[px] = -value[x] + value[y] + s;
	}

关于合并操作的那个公式:
举个例子,已知x所在的并查集根节点为px,y所在的并查集根节点为py,如果有了x、y之间的关系,要将px并到py上,如果不考虑权值直接修改parent就行了,即parent[px]=py即可;
但是现在是带权并查集, 必须得求出px与py这条边的权值是多少,使得 x到px的权值+px到py的权值=x到y的权值+y到py的权值,又由题干知道x到y的权值是s,由此得出公式s+value[y]=value[x]+value[px];我们要更新的是px,就有value[px]=-value[x]+value[y]+s;

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <queue>
#include <stack>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,m;
int f[maxn],dist[maxn],ans;
int find(int u)
{
	if (f[u]==u)
		return u;
	int tmp=find(f[u]);
	dist[u]+=dist[f[u]];
	f[u]=tmp;
	return f[u];

}
int merg(int u,int v,int w)
{
	int t1=find(u),t2=find(v);
	if (t1==t2&&dist[v]!=dist[u]+w)
		return 1;

	f[t2]=t1;
	dist[t2]=dist[u]+w-dist[v];
	return 0;
}
int main()
{
	int i,j,x,y,z;
	while(~scanf("%d%d",&n,&m))
	{
		for (i=1; i<=n; i++)
		{
			f[i]=i;
			dist[i]=0;
		}

		ans=0;
		while(m--)
		{
			scanf("%d%d%d",&x,&y,&z);
			if (merg(x,y,z))
			{
				ans++;
			}
		}
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/shidianshixuan/p/14386336.html