最近学习了并查集,第一印象就是模板,接触之后发现还有种类并查集这种东西,当初看一个大佬推公式的帖子看到自闭,不过理解了原理之后遇到这种题可以自己推公式,也是很开心了。
总的来说,种类并查集比普通并查集多了一个数组,这里暂时用re,这个数组是用来记录每个节点和根节点的关系的。我们可以自己设定值来表示不同的关系。这里给出一道题做例子:
https://vjudge.net/contest/279444#problem/J
题意大概就是给出两只情侣Bug,分别是不同的性别,我们根据之前给出的案例判断有没有同性恋....
首先,并查集是要用到的,把有关系的bug都归到一个集合,其次,判断性别,这里就需要多加一个re来判断了,re代表节点与根节点性别是否相同,我们设0是相同,1是不同。
首先,我们要将re初始化为0,也就是自己和自己性别相同。
现在可以思考了,我们在合并以及路径压缩时,要随着父节点的变化改变re的值,
路径压缩时,最多只有三个级别,毕竟这个是走一步压缩一步,之前的都压缩成两级了。
x的父节点时rootx,rootx的父节点是rootxx;
这里要将x直接指向rootxx,有这样的思路:x->rootx+rootx->rootxx=(re[x]+re[rootx])%2.
在合并时,也要注意关系的更新
rootx=find(x);
rooty=find(y);
如果rootx,rooty不相同,则将rootx连接到rooty上,并且更新关系,我们知道,进行了路径压缩之后最多只有两个级别。
需要注意的是,re[x]是rootx与x的关系值。
rootx->rooty=rootx->x+x->y+y->root=(re[x]+1+root[y])%2;
这时x,y不在同一集合,也就是第一次出现,自然无法检测是不是同性,所以x,y之间的值是1。
我们只需要在路径压缩模块和合并单元里加上这两个关系更新的代码就好啦!
需要自己解决的是,关于数的设定,需要自己用心想一想啦,怎样设定才能达到目的。
贴个代码
#include<stdio.h>
int flag;
struct ppp
{
int dad,re;
}k[2010];
int chushi(int n)
{
for(int i=1; i<=n; i++)
{
k[i].re=0;
k[i].dad=i;
}
}
int find(int x)
{
int temp=k[x].dad;
if(temp!=x)
k[x].dad=find(k[x].dad);
k[x].re=(k[x].re+k[temp].re)%2;
return k[x].dad;
}
int join(int a,int b)
{
int x,y;
x=find(a);
y=find(b);
if(x!=y)
{
k[y].dad=x;
k[y].re=(k[b].re+1+k[a].re)%2;
}
if(x==y)
{
if(k[a].re==k[b].re)
flag=1;
}
}
int main()
{
int t,n,m,a,b;
scanf("%d",&t);
int ans=0;
while(t--)
{
ans++;
flag=0;
scanf("%d %d",&n,&m);
chushi(n);
for(int i=0; i<m; i++)
{
scanf("%d %d",&a,&b);
join(a,b);
}
printf("Scenario #%d:\n",ans);
if(flag==1)
printf("Suspicious bugs found!\n");
else
printf("No suspicious bugs found!\n");
if(t!=0)
printf("\n");
}
}
食物链题比这个复杂,可以自己思考一下,附上链接:https://vjudge.net/contest/279444#problem/E
仔细思考哦!
如果没有思路的话再来看这篇博客吧:https://blog.csdn.net/qq_43644454/article/details/86536452