好久没有ACM,对很多地方都有胆怯,看着并查集资料,这道题摸索了4个小时,可见编码能力有待大幅提高。
首先想到思路无疑是按题目中A,B,C分类方式,维护多个集合,再判断集合关系,适当合并。这种做法很直观,但却很麻烦。麻烦主要出现在维护集合间关系。当两个集合合并后,与合并集合相关集合的关系需要递归的维护。例如A-B, C-D,合并A,C后,B,D也需要合并。以元素抽象的集合操作麻烦。最后看题解上,维护的是有关系元素的集合,而不是同类型元素集合,并在关系集的结点中用相对偏移维护结点和根关系。合并时,更新根结点关系,并在查找时更新结点关系。详细内容都可参考网络上大部分实现。
理解了这种做法后,我不想用路径压缩,而想用相对关系树来做。合并时更新作为孩子的结点的关系偏移量,在判断关系时通过遍历从结点到根的关系偏移量得到结点和总的根结点关系。
well 代码分格越来越简练,明了。
less well 较费时的地方:合并根结点计算相对偏移。mod运算结果是负数。差错用了不少时间。
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 50001
typedef struct _Node{
int p;
int r;
}Node;
Node elem[MAXSIZE];
int N,K;
void initial()
{
int i;
for(i=1;i<=N;i++)
{
elem[i].p=i;
elem[i].r=0;
}
}
int find(int x)
{
while(x!=elem[x].p)
x = elem[x].p;
return x;
}
int relation(int x)
{
int r=0;
while(x!=elem[x].p){
r += elem[x].r;
x = elem[x].p;
}
return r%3;
}
int merge(int x, int y, int px, int py, int type)
{
elem[px].r = (relation(y)-type-relation(x)+3)%3;
elem[px].p = py;
}
int judge(int t,int x,int y)
{
int px,py;
if((x>N)||(y>N)) return 0;
if(t==1)
{
px = find(x);
py = find(y);
if(px!=py) {merge(x,y,px,py,0); return 1;}
return (relation(x)==relation(y));
}
else
{
px = find(x);
py = find(y);
if(px!=py) {merge(x,y,px,py,1); return 1;}
//printf("%d==%d\n", ( relation(x)+1 ) %3, relation(y));
return ( (( relation(x)+4) %3) == relation(y) );
}
}
int main()
{
int t, x, y;
int i,j;
int w;
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d",&N,&K);
initial();
w=0;
for(i=0;i<K;i++)
{
scanf("%d%d%d",&t,&x,&y);
if(!judge(t,x,y))
{
//printf("t:%d,x:%d,y:%d\n",t,x,y);
//for(j=1;j<=5;j++)
//{
// printf("j=%d,p=%d,r=%d\n",j,elem[j].p,elem[j].r);
//}
w++;
}
}
printf("%d\n",w);
return 0;
}