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\n",ans);
}
return 0;
}