这道题目,理解了好久,其实只要把关系图画一下,有些概念自然就知道了……
当然也使我明白了,并查集因为通过路径压缩后,一个节点的find()过程,最多需要2次递归,最多一次修改……这样题目就理解了……
View Code
#include <stdio.h>
#define max 50000+5
int f[max];
int r[max];
/*
rank[x]表示father[x]与x的关系
rank[x] == 0 表示father[x]与x是同类
rank[x] == 1 表示x吃father[x]
rank[x] == 2 表示father[x]吃x
*/
void make_set(int x)
{
f[x]=-1;
r[x]=0;
}
int find_set(int x)
{
if (f[x]==-1) return x;
int t=f[x];
f[x]=find_set(t);//路径压缩
r[x]=(r[t]+r[x])%3;
//important. 更新r[]数组中x与代表元素的相对关系。更新原因:
//代表元素在union_set操作中被改变了。
//至于这个式子的推得.可以枚举rx与p[x], p[x]与x的关系,然后观察得到。
//更好的方法是向量运算。
return f[x];
}
void union_set(int x,int y,int d)
{
int a=find_set(x);
int b=find_set(y);
f[a]=b;
r[a] = (r[y] - r[x]+ 2 + d ) % 3;
//同上。这两个关系的推得实际上是这道题的关键所在。
}
int main()
{
int n,k;
int d,x,y,lie=0;
scanf("%d%d",&n,&k);
for (int i=0;i<max;i++)
make_set(i);
while (k--)
{
int a,b;
scanf("%d%d%d",&d,&x,&y);
if (d==2&&x==y) {lie++;continue;}
if (x>n||y>n) {lie++;continue;}
a=find_set(x);
b=find_set(y);
if (a==b)
{
if (d==1&&r[x]!=r[y])
lie++;
else
{
if (d==2&&r[x]!=(r[y]+1)%3)
lie++;
}
}
else
union_set(x,y,d);
}
printf("%d\n",lie);
return 0;
}