带权并查集(深度理解)

我们都知道一个点的权值,是这个节点到根节点之间的差值,我们根据差值,就可以知道很多东西,权值数组sum[maxn]

1.已知两个点的权值(假设都是与根节点的权值),那么这个两个点的差值为   sum[x]-sum[y]=(fen[x]-u)-(fen[y]-u)=fen[x]-fen[y] 就是参考点变换了一下,就可以间接找出这两个节点之间的差值。

2.在合并两个点时,  我们已经知道了,这个权值就是这个点与根节点之间的差值。我们可以根据这个关系,在合并集合的时候,找出两个集合的根节点之间的差值。

我们现在假设定义 pre[fy]=fx 就是将fy连到fx上,我们 就定义fy 比 fx 高多少x(或者高 -x ),然后pre[fx]=fy 就是fx 比fy 高什么。我们都先定义一个方向,在后面好理解 权值之间来回的关系。上面 第一条已经说了,权值就是将参照点变了一下,我们 通常x比y高,也可以间接的来说,x先比a高 10分,a又比b高10分,b又比c高 10分 ,c又比y高10分,现在我们定义一个方向矢量,由x直接到y的那个矢量就是由x到a再到b再到c最后再到y,那么这个值全加上,就是x直接与y之间的 差值(就是权值40),我们可以利用这个性质,可以来理解带权并查集中的难点。

就是下面这个地方容易搞混,其实将方向矢量加入到这个思考中,就会想的简单一点,我们现在已经都设置了方向,由px->py ,就是px比py 高多少,py->px 就是py比px 高多少(也是权值之间的差值),所以下面这些式子都是根据在这个方向的确立之上的,由x直接到px ,等价于由x间接到y->py->px,这样将中间步骤的差值都加上去,就可以找出 px->py 之间差值,或者py->px 之间差值。

void join(int x,int y,int s)
{
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy)
	{
		pre[fy]=fx;
		sum[fy]=sum[x]-sum[y]-s;
		
		//pre[fx]=fy;
		//sum[fx]=-sum[x]+sum[y]+s;
	}
}
//不管上面是谁指向谁,下面求出这个a,b之间差值都是这样求得,不用改变。毕竟我们已经指定 a->b了。
scanf("%d%d",&a,&b);
int fa=find(a);
int fb=find(b);
if(fa!=fb)
	cout<<"-1"<<endl;
else
{
	cout<<sum[a]-sum[b]<<endl;
}

真的是很巧妙。

分数调查

HihoCoder - 1515    

Zjnu Stadium

HDU - 3047

How Many Answers Are Wrong

HDU - 3038

都会涉及这个应用,是很重要的一个结论吧。

发布了123 篇原创文章 · 获赞 83 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/89429238