种类并查集(POJ 1703)

时间:2023-03-09 16:47:40
种类并查集(POJ 1703)

1703 -- Find them, Catch them

http://poj.org/problem?id=1703

题目大意:有2个敌对帮派,输入D a b表示a,b在不同帮派,输入A a b表示询问a,b是否是在一个帮派。

题解:因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们就把这2个元素之间的关系量转化为一个偏移量,

假设
x->y 偏移量0时 x和y同帮派

x->y 偏移量1时 x和y不同帮派

不妨继续假设,x的当前集合根节点tx,y的当前集合根节点ty。

如果tx和ty不相同,那么我们把ty合并到tx上,并且更新deltx[ty]值(注意:deltx[i]表示i的当前集合根节点到i的偏移量!!!!)

此时 tx->ty = tx->x + x->y + y->ty,可能这一步就是所谓向量思维模式吧

上式进一步转化为:tx->ty = (deltx[x]+1-deltx[y])%2 = deltx[ty],(模2是保证偏移量取值始终在[0,1]间)

接下来把这个想法再运用到路径压缩中:

ffx

|     \

fx      \

|         /

|     /

x

路径压缩过程中会将fx的父亲变为x的父亲,所以要改变相对关系,即偏移量。

ffx->fx+fx->x=ffx->x;

转换成:delta[x]=(delta[fx]+delta[x])%2;

int find(int x)
{
  if(x==fa[x])
    return fa[x];
  int f=fa[x];
  fa[x]=find(fa[x]);
  delta[x]=(delta[x]+delta[f])&1; //这句和上句不能交换位置,delta[]的改变是随着路径压缩是fa[]的改变而改变
  return fa[x];
}

#include <stdio.h>
int n;
int fa[],delta[]; //deltx[i]表示i的当前集合根节点到i的偏移量
void init()
{
int i;
for(i=;i<=n;i++)
{
fa[i]=i;
delta[i]=;
}
} int find(int x)
{
if(x==fa[x])
return fa[x];
int f=fa[x];
fa[x]=find(fa[x]);
delta[x]=(delta[x]+delta[f])&; //这句和上句不能交换位置,delta[]的改变是随着路径压缩是fa[]的改变而改变
return fa[x];
} void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
fa[fy]=fx;
delta[fy]=(delta[x]+-delta[y])&;
}
}
int main()
{
int t,m,i,a,b;
char ch;
scanf("%d",&t);
while(t--)
{ scanf("%d%d%*c",&n,&m); //%*c替掉回车,ch吃回车,经常搞错,哎。。。
init();
for(i=;i<=m;i++)
{
scanf("%c%d%d%*c",&ch,&a,&b);
if(ch=='D')
merge(a,b);
if(ch=='A')
{
if(find(a)==find(b))
{
if(delta[a]==delta[b]) //根节点到a和b的偏移量相同时,a和b必定为同一帮派。朋友的朋友是朋友,敌人的敌人也是朋友
printf("In the same gang.\n");
else
printf("In different gangs.\n");
}
else
printf("Not sure yet.\n");
} } }
return ;
}