原文地址:http://www.cnblogs.com/ffjjqqjj/archive/2011/07/20/2112226.html
做的第一道并查集题。
这题揪心一整天了,网上搜了一下题解才大概明白,明白了之后感觉思路还算是简单,就是集合间关系的公式的推导很重要。
/******************************************************************************
顺便了解了一下并查集。
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合 ,也就是按一定顺序将属于同一组的元素所在的集合合并。 把每个点所在集合 初始化为其自身。 通常来说,这个步骤在每次使用该数据结构 时只需要执行一次,无论何种实现方式,时间复杂度 均为O(N)。查找
查找元素所在的集合,即根节点。
合并
将两个元素所在的集合合并为一个集合。
**************************************************************************************************/
p(x)为x所属树的根节点。kind(x)为x与该树根节点的关系。height(x)为树x的高度。
现在规定:
0:同类关系。
1:吃关系。
2:被吃关系。
我们用x--r-->y表示x和y之间的关系是r,比如x--1--y代表x吃y。现在,若已知x--r1-->y,y--r2-->z,如何求x--?-->z?,于是我们不难发现,x--(r1+r2)%3-->z。有了这个结论,我们就可以做题了。
我们初始化kind(1-N)=0,表示自己和自己为同类。P(1-N)=i,代表各个节点都是一棵树。当D X Y时,则应合并X的根节点和Y的根节点,同时修改各自的kind。那么问题来了,合并了之后,被合并的根节点的kind值如何变化呢?
现有x和y,d为x和y的关系,px和py分别是x和y的根节点,于是我们有x--kind[x]-->px,y--kind[y]-->py,显然我们可以得到px--(3-kind[x])-->x,py--(3-kind[y])-->y。假如合并后px为新的树的根节点,那么原先px树上的节点不需变化,py树则需改变了,因为kind值为该节点和树根的关系。这里只改变kind(py)即可,因为在进行find_set操作时可相应改变py树的所有节点的kind值。于是问题变成了py--?-->px。我们不难发现py--(3-kind[y])-->y--(3-d)-->x--kind[x]-->px,根据前面的结论,我们有py--(3-kind[y]+3-d+kind[x])%3-->px。我们求解了px和py的关系了。
代码如下:
#include<stdio.h>
int father[50001];
int kind[50001];
int height[50001];
int Find(int x)
{
if(father[x] == x)
return x;
int t;
t = father[x];
father[x] = Find(father[x]);
kind[x] = (kind[x] + kind[t]) % 3;
return father[x];
}
void Union(int px, int py, int x, int y, int d)
{
if(height[px] < height[py]){
father[px] = py;
kind[px] = (3-kind[x]+d+kind[y]) % 3 ;
}
else{
if( height[px] == height[py] )
height[px]++;
father[py] = px;
kind[py] = ( 3 - kind[y] + 3 - d + kind[x]) % 3;
}
}
int main()
{
int i,sum,n,t,d,x,y,px,py;
scanf("%d %d", &n, &t);
for(i=1; i<=n; i++)
{
father[i] = i;
kind[i] = height[i] = 0;
}
sum = 0 ;
while(t--)
{
scanf("%d%d%d", &d, &x, &y);
if(x>n || y>n ) {
sum++;
continue;
}
if( d==2 && x==y) {
sum++;
continue;
}
px = Find(x);
py = Find(y);
if(px == py) {
if(d==1 && kind[x]!=kind[y]) {
sum++;
continue;
}
if(d==2 && (kind[x]+3-kind[y])%3 != 1){
sum++;
continue;
}
}else{
Union(px, py, x, y, d-1);
}
}
printf("%d\n", sum);
getchar();
return 0;
}