poj 1703 Find them, Catch them

题目链接:http://poj.org/problem?id=1703

题目大意:警察抓获N个罪犯,这些罪犯只可能属于两个团伙中的一个,现在给出M个条件(D a b表示a和b不在同一团伙),对于每一个询问(A a b)确定a,b是不是属于同一团伙或者不能确定。

思路:一般的并查集题目都是给出a,b属于同一集合,但是这题不同,给出的a,b不在同一集合,如果用其他方法可能太复杂,在此介绍一种此类问题的通法:

定义数组pre[x]表示x的父节点,r[x]表示x与当前所在集合的代表元的关系,0表示x与代表元属于同一团伙,1表示不在同一团伙,初始值都为0,因为自己肯定和自己在同一团伙。

 

合并两个元素a,b的时候:

void Union(int x,int y)
{
        int xx=find(x);          //  1
        int yy=find(y);         //  2
        pre[xx]=yy;             //  3
        r[xx]=(r[y]+r[x])^1;   //  4
}

对于1,2,3句话,不用解释,与一般的并查集合并操作一样(没用启发式合并),而对于第4句话,既然把代表元为xx的集合合并到了代表元为yy的集合,那么xx与他现在的集合的代表元(也就是yy)的关系r[xx]肯定是要改变的,至于怎么改变,可以枚举所有情况然后找出规律(听说可以用向量方法想,但是现在不懂)。同样的,原来以xx为代表元的集合中的所有元素的r[]值都可能发生改变,那么在此是不是要把所有的元素的r都改变一次呢,答案是否定的,我们可以再find()操作里面改变。

 

查找操作:

int find(int x)
{
 if(x!=pre[x])
 {
  int f=pre[x];                  // 1
  pre[x]=find(pre[x]);
  r[x]=(r[x]+r[f])%2;       //  2
 }
 return pre[x];
}

第1句话,先把原来x的父节点保存起来,然后路径压缩的时候就已经把r[pre[x]]修改了,然后接下来就是修改r[x],r[x]的值与r[f]的值的关系很好推,就是r[x]=(r[x]+r[f])%2。


#include"stdio.h"
int f[100005];
int r[100005];
void set(int n)
{
	int i;
	for(i=1;i<=n;i++)
	{
		f[i]=i;r[i]=0;
	}
}
int find(int x)
{
	int t;
	if(x!=f[x])
	{
		t=f[x];
		f[x]=find(f[x]);
		r[x]=(r[x]+r[t])%2;
	}
	return f[x];
}
void Union(int x,int y)
{
	int xx,yy;
	xx=find(x);
	yy=find(y);
	f[xx]=yy;
	r[xx]=(r[x]+r[y])^1;
}
int main()
{
	int a,b,t,n,m,aa,bb;
	char s[3];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		set(n);
		while(m--)
		{
			scanf("%s%d%d",s,&a,&b);
			if(s[0]=='A')
			{
				aa=find(a);bb=find(b);
				if(aa==bb&&r[a]!=r[b])
					printf("In different gangs.\n");
				else if(aa==bb&&r[a]==r[b])
					printf("In the same gang.\n");
				else
					printf("Not sure yet.\n");
			}
			else Union(a,b);
		}
	}
	return 0;
}



posted on 2012-07-19 19:33  Slege  阅读(84)  评论(0编辑  收藏  举报

导航