本题也是一道经典的并查集题目,只不过并不是直来直去的,因为需要维护两组关系:同一伙、不是同一伙。
那要怎么办呢,一开始我用了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; }