雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

三个种类的并查集——pku1182(中上难度)

Posted on 2011-02-27 15:33  huhuuu  阅读(310)  评论(0编辑  收藏  举报
这道题目,理解了好久,其实只要把关系图画一下,有些概念自然就知道了……
当然也使我明白了,并查集因为通过路径压缩后,一个节点的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;
}