本题也是一道经典的并查集题目,只不过并不是直来直去的,因为需要维护两组关系:同一伙、不是同一伙。
那要怎么办呢,一开始我用了vector来保存对立面,建立两个并查集,结果很自然是超时的,因为循环了太多次。
之后看别人的题解,感觉有点复杂,看的不是很明白,只有一篇给出了一个简单的方法,然后我又重新看了食物链那道题,发现本题就是那道题的改编,可以使用相同的方法来维护多组关系。
就像食物链那道题一样,对每个人创建两个元素:i和i+n。用3*n个元素来建立并查集,这个并查集可以维护如下信息:
如果x和y不是同一个帮派的,就合并想x和y+n、y和x+n。 这样以后假设有个z,他和x不是一伙的,那么就合并x和z+n、z和x+n又因为x+n与y在一组里所以y与z自然也在一组。
这样以后对于给出的任意两个人a、b,只要他们在一组里,就说明在一个帮派,如果a、b+n在一组,就说明他们不在一个帮派,否则不能确定。
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int T,n,m,a,b,par[100005],rankk[100005];
void init(int n) {
for(int i=1;i<=n;i++) {
par[i] = i;
rankk[i] = 0;
}
}
int findd(int x) {
return par[x] == x ? x : par[x] = findd(par[x]);
}
void unite(int x,int y) {
x = findd(x);
y = findd(y);
if(x==y) return ;
if(rankk[x] < rankk[y]) {
par[x] = y;
} else {
par[y] = x;
if(rankk[x] == rankk[y]) rankk[x]++;
}
}
bool same(int x,int y) {
return findd(x) == findd(y);
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
char s[10];
init(n*2);
for(int i=0;i<m;i++) {
scanf("%s",s);
scanf("%d%d",&a,&b);
if(s[0]=='D') {
unite(a,b+n);
unite(a+n,b);
}
else {
if(same(a,b)) printf("In the same gang.\n");
else if(same(a,b+n)) printf("In different gangs.\n");
else printf("Not sure yet.\n");
}
}
}
return 0;
}