一道欠了很久的题目,终于解决了
之前一直以为,将父节点设置为自己然后 用 fa【i】==i 来判断是不是根节点的方式很蠢,都不能记录这个集合有多少个元素,而且搜索路径会边长,不能依据集合大小来判断是哪个集合并上哪个集合,实际上还是自己太无知,不知道有路径压缩这种东西来改变并查集的fa数组的结构。
路径压缩就是在每次 find(x) 函数执行之后 在返回根节点的时候将 fa【x】=root 这样一来就能解决搜索路径的问题,
然后再添加一个d数组, 是用于解决带边权 ,我们的权值则是一般记录此节点与父节点的关系,只要满足这个关系可以传递我们就可以模仿矢量计算来处理权值。
d【x】数组存储的是x节点和他的根节点的关系,在每次find 更新fa【x】的时候一起更新。
#include<stdio.h>
#include<iostream>
#define max 50000
using namespace std;
int fa[max];
int d[max];
int find(int x){
if(x==fa[x]) return x;
int root =find(fa[x]);
d[x]=(d[x]+d[fa[x]])%3;
return fa[x]=root;
}
int main(){
int n,m;
int a,b,c,cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
fa[i]=i;
d[i]=0;
}
while(m--){
scanf("%d%d%d",&a,&b,&c);
if((b>n||c>n)||(a==2&&b==c)){
cnt++;
continue;
}
if(a==1&&b==c)
continue;
int bb=find(b);
int cc=find(c);
if(bb==cc){
if(a==1&&d[b]==d[c]){
;
}
else if(a==2&&(d[b]+(3-d[c]))%3==1){
;
}
else {
cnt++;
// printf("%d %d %d !!!\n",a,b,c);
// printf("%d %d!!!\n",d[b],d[c]);
}
}
else{
fa[bb]=cc;
d[bb]=(3-d[b]+a-1+d[c])%3;
}
}
printf("%d\n",cnt);
return 0;
}