Find them, Catch them.(POJ-1703)(并查集)

本题也是一道经典的并查集题目,只不过并不是直来直去的,因为需要维护两组关系:同一伙、不是同一伙。

那要怎么办呢,一开始我用了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;
}


你可能感兴趣的:(ACM,poj)