题意 :有两个不同的帮派,每个帮派至少有一个人。 判断两个人是否属于同一个帮派。有 T 组测试数据,给你 N 个人,编号从 1 到 N,操作 M 次。每次操作输入一个字符和两个数 x ,y ,如果字符为 A 则判断 x 和 y 是否属于同一个帮派,并且输出结果。 如果字符为 D 则明确告诉你 x 和 y 是属于不同帮派的。
分析:并查集,以前接触的并查集都是让我们判断是否属于同一个连通分量,但这道题却让你判断是否属于同一类。
除了像普通的并查集定义一个 p[] 记录父亲节点外,还定义一个 r[] 记录当前点与其所属的连通分量的根节点的关系。
r[] = 0 表示属于同一个帮派; r[] = 1表示与其根节点属于不同的帮派。
开始时初始化自己是自己的父亲 p[x] = x,自己与自己属于同一类 r[x] = 0.一旦输入 D 断定 x 和 y 属于不同集合后,就连接 x 和 y 所在的树,同时更新 r[]一旦输入 A如果 find(x) 不等于 find(y) 说明还没有判断过 x 与 y 直接输出关系不确定即可Not sure yet.如果find(x)等于 find(y) ,但是他们的r不等,说明属于不同帮派,输出In different gangs.如果他们的r相等,说明属于同一个帮派,则输出In the same gang
注意:1.find()函数寻找根节点的时候要不断的更新 r
根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
如果 a 和 b 的关系是 r1, b 和 c 的关系是 r2,
那么 a 和 c 的关系就是 (r1+r2)%2 . PS:因为只用两种情况所以对 2 取模。
如果实在不好理解,那么我们就枚举推理一下,共有 2*2 = 4种情况:
(a, b) (b, c) (a, c) (r1+r2)%2
0 0 0 0 a 和 b是同类 , b 和 c 是同类, 所以 a 和 c 也是同类
0 1 1 1 a 和 b是同类 , b 和 c 是异类, 所以 a 和 c 也是异类
1 0 1 1 a 和 b是异类 , b 和 c 是同类, 所以 a 和 c 是异类
1 1 0 0 a 和 b是异类 , b 和 c 是异类, 所以 a 和 c 是同类
2.Union()联合两棵树的时候也要更新两棵树的根的关系
定义:fx 为 x的根节点, fy 为 y 的根节点
联合时,使得 p[fx] = fy; 同时也要寻找 fx 与 fy 的关系。关系为:(r[x]+r[y]+1)%2
如何证明?
fx 与 x 的关系是 r[x],
x 与 y 的关系是 1 (因为确定是不同类,才联合的),
y 与 fy 关系是 r[y],模 2 是因为只有两种关系
所以又上面的一点所推出的定理可以证明 fx 与 fy 的关系是: (r[x]+r[y]+1)%2
#include <cstdio>
#include <iostream>
#include <cstring>
#define MAXN 100050
using namespace std ;
int p[MAXN] ; //存贮父节点
char r[MAXN] ; //存储与根节点的关系,0代表同类,1代表不同类
int
Find_Father ( int x )
{
if ( x != p[x] )
{
int temp ; //记录父节点,方便更新下面r[]
temp = p[x] ;
p[x] = Find_Father ( p[x] ) ;
r[x] = ( r[x] + r[temp] ) % 2 ; //根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
}
return p[x] ;
}
void
Union ( int const x , int const y )
{
int pre_x , pre_y ;
pre_x = Find_Father ( x ) ;
pre_y = Find_Father ( y ) ;
p[pre_x] = pre_y ;
r[pre_x] = ( r[x] + r[y] + 1 ) % 2 ; //pre_x与x关系 + x与y的关系 + y与pre_y的关系 = pre_x与pre_y的关系
return ;
}
void
Set ( int const n )
{
for ( int i = 0 ; i <= n ; i ++ )
{
p[i] = i ; //自己是自己的父节点
r[i] = 0 ; //自己和自己属于同一类
}
}
void
Solve ( )
{
getchar ( ) ; //吃掉回车
char ch ;
int x , y ;
scanf ("%c%d%d" , & ch , & x , & y ) ;
if ( 'A' == ch )
{
if ( Find_Father(x) == Find_Father(y ) ) //如果根节点相同,则表示能判断关系
{
if ( r[x] != r[y] )
{
printf ("In different gangs.\n") ;
}
else
{
printf ("In the same gang.\n") ;
}
}
else
{
printf ("Not sure yet.\n") ;
}
}
else
{
Union ( x , y ) ;
}
return ;
}
int
main ( )
{
int T ;
scanf ("%d" , & T ) ;
while ( T-- )
{
int n , m ;
scanf ("%d%d" , & n , & m ) ;
Set ( n ) ;
while ( m -- )
{
Solve ( ) ;
}
}
return 0 ;
}