首先介绍一下什么是带权并查集
带权故名思意,在一个集合中,集合之间两两距离为权值,它与普通的的并查集主要差在find函数上
对比
普通
int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
带权
int find(int x)
{
if(fa[x].fa != x)
{
int t=fa[x].fa;
fa[x].fa = find(fa[x].fa);
fa[x].wz+=fa[t].wz;
}
return fa[x].fa;
}
fa[x].wz+=fa[t].wz;很显然他们之间的差别在这
这句话的意思是x这个数在集合中的位置是它本身的位置加上它父亲所在的位置。
可能有的朋友会问,那岂不是每一次find查找都会进行一次加,然后x的位置随着每一次find不断增加,这不对了,这就说明我们对并查集find的这个函数理解不够。
并查集可以类似看成多叉数,它们在每一次合并时都可以看成把它和根节点相连,我们赋根位置为零,所以在第一次查找之后,每次都是它本身的位置加零(因为它父亲是根节点),位置大小并不会改变。
例题
#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
int fa,wz,size;
};
node fa[30002];
void csh(){
for(int i=1;i<=30001;i++)
{fa[i].fa=i;
fa[i].wz=0;//位置刚开始都是零
fa[i].size=1;//每一个集合大小开始都为一
}
}
int find(int x)
{
if(fa[x].fa != x)
{
int t=fa[x].fa;
fa[x].fa = find(fa[x].fa);
fa[x].wz+=fa[t].wz;//重点
}
return fa[x].fa;
}
int main()
{
csh();
scanf("%d",&n);
while(n--)
{
char command;
cin>>command;
int x,y;
scanf("%d%d",&x,&y);
int xx,yy;
xx=find(x);
yy=find(y);
if(command == 'M')
{
fa[xx].fa=yy;
fa[xx].wz+=fa[yy].size;//xx为x所在集合的根节点,xx距离y所在集合的根节点的距离为y集合的大小加上xx本身的位置(由于xx为根节点,本身位置为0)
fa[yy].size+=fa[xx].size;//更新yy集合的大小
fa[xx].size=0;//更新xx集合的大小
continue;
}
if(command == 'C')
{
if(yy!=xx)
printf("-1\n");
else
printf("%d\n",abs(fa[x].wz-fa[y].wz)-1);
continue;}
}
return 0;
}