题意:即给出存在势力不同的帮派a,b(两个不相交的集合);然后给出某序号之间关系:帮派不同(互斥),然后在根据已给出的信息判断某两个成员之间是否在同一个帮派(某两个元素是否相交,或者关系无法确定)
思路:利用并查集的特性,(不明确给出关系,利用元素之间的关系可以得到集合之间的信息).
同时此题也是 POJ-1182 食物链的 简化版本(那道题给出三个集合关系,如果用此题的思路就是开三倍的集合大小)
由于此题给的是 两个成员间为 互斥属性,所以就思考 :
有n个成员,开两倍空间(即假设某个成员有两面的属性),1~n为一组, n+1~2n为一组。a与b互斥,则a与b反(即b+n)为同一集合,同时b与a反(a+n)为同一集合,这样就确立联系.同时在union操作中,我们引入rank(相当于模拟某个作为根的结点“高度”,高度越高意味着他作为根连接的其他结点越多,就更加容易连接)
完整代码:
#include <iostream>
#include <cstdio>
#include <cstring>
const int maxn = 1e5+7;
using namespace std;
int pre[2*maxn];
int rank[2*maxn];
int find(int x)
{
if (x == pre[x]) return x;
else return pre[x] = find(pre[x]);
}
void unite(int x, int y)
{
int px = find(x), py = find(y);
if (px == py) return ;
else if (rank[px] > rank[py])
{
pre[py] = px;
}
else
{
pre[px] = py;
if (rank[px] == rank[py]) rank[py]++;
}
}
bool same(int x, int y)
{
int px = find(x), py = find(y);
return px == py;
}
int main()
{
int T, n, m;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for (int i = 1;i <= 2*n; i++)
{
pre[i] = i;
rank[i] = 1;
}
char ch;
int a, b;
for (int i = 0; i < m; i++)
{
cin>>ch;
scanf("%d%d", &a, &b);
if (ch == 'A')
{
if ( same(a, b) || same(a, b+n) )
{
if ( same(a, b) ) printf("In the same gang.\n");
else printf("In different gangs.\n");
}
else printf("Not sure yet.\n");
}
else
{
unite(a, b+n);
unite(a+n, b);
}
}
}
return 0;
}