并查集水题。循环路径并查集
关键是合并关联集合,分析如下:现有关系A a,b(a与b属于不同集合)
那么现在要做的就是将a关联集合与b关联集合和并,问题:如何合并?
必定是对父节点合并,那么转换关系如何?
分析如下:
路径压缩后,令deep[a]表示a的深度,其根节点为x,令deep[b]表示b的深度,其根节点为y,那么若将x集合与y集合合并(假设合并后根节点为x),则合并后b的深度应该为:
deep[b]=(deep[a]+1)%2;而此时deep[b]=deep[y]+deep[b],故有deep[y]+deep[b]=(deep[a]+1)%2;也即deep[y]=(deep[a]+1-deep[b])%2,这样就将x集合和y集合合并在一起了,这是核心,接下来主函数就只需要判断深度关系即可判断对应的关系。典型的循环路径压缩并查集。
下面是代码:940K+360MS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 100010
#define Maxx(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
int set[Max];
int deep[Max];
int c,n,m;
int find(int x){
if(x==set[x])
return x;
int temp=find(set[x]); // 递归寻找根节点
deep[x]=(deep[x]+deep[set[x]])%2; //更新深度
set[x]=temp; //路径压缩
return temp; //返回根节点
}
int main(){
scanf("%d",&c);
while(c--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){ //初始化深度为0
set[i]=i;
deep[i]=0;
}
int a,b,x,y;
while(m--){
getchar();
char temp=getchar();
if(temp=='A'){ //若为A,则检查关系
scanf("%d%d",&a,&b);
x=find(a),y=find(b);
if(x!=y) //若还没有产生关系
printf("Not sure yet.\n");
else{ //否则必定存在关系
if(deep[a]!=deep[b]) // 若深度相同,则说明是同类
printf("In different gangs.\n");
else //否则不是同类
printf("In the same gang.\n");
}
}
else{
scanf("%d%d",&a,&b);
x=find(a),y=find(b); //查找根节点
if(x<y){ //大并小
deep[y]=(deep[a]+1-deep[b])%2; //路径更新
set[y]=x;
}
else if(y<x){
deep[x]=(deep[b]+1-deep[a])%2;
set[x]=y;
}
}
}
}
return 0;
}