题目来源:http://poj.org/problem?id=1703

Description

The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.) 

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds: 

1. D [a] [b] 
where [a] and [b] are the numbers of two criminals, and they belong to different gangs. 

2. A [a] [b] 
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang. 

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

Output

For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output

Not sure yet.
In different gangs.
In the same gang.

题意:

给你一些操作数:“字母A或者D” 数字a和b

A表示询问a和b关系,如果是同一类就输出“In the same gang.”,如果不是同一类就输出“In different gangs.”,不确定这输出“Not sure yet.”

思路:

类似于食物链

1:只要维护不用种类之间的关系即可,如果a和b不是一类就让a和b+n是一类,b和a+n是一类,查询是候直接判断就好(详细看代码)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include<vector>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define ll long long
#define N 100005
#define same(u,v) Find(u)==Find(v)

using namespace std;
int pre[N<<1],re[N<<1];
int T,n,m,a,b;
char c;
void init()//初始化
{
    for(int i=1; i<=2*n; i++)
    {
        pre[i]=i;
        re[i]=0;
    }
}
int Find(int x)
{
    return x==pre[x]? x : pre[x]=Find(pre[x]);
}
void Join (int u, int v)
{
    int fu,fv;
    fu=Find(u);
    fv=Find(v);
    if(fu==fv)
        return;
    if(re[fv]>re[fu])
        pre[fu]=fv;
    else
    {
        pre[fv]=fu;
        if(re[fu]==re[fv])
            re[fu]++;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        for(int i=0; i<m; i++)
        {
            getchar();
            scanf("%c %d %d",&c,&a,&b);
            if(c=='D')//a和b不是一类让a和b+n是一类,b和a+n是一类
            {
                Join(a,b+n);
                Join(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;
}

2:处理种类直接的关系:将两个集合的所有及节点合并到一个并查集中,通过数组re表示与根节点或者父节点是否相同

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include<vector>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
#define N 100005
#define same(u,v) Find(u)==Find(v)
using namespace std;
int pre[N<<1],re[N<<1];                // re=1表示与父节点不属于同一集合
int T,n,m,a,b;
char c;
void init()
{
    for(int i=1; i<=2*n; i++)
    {
        pre[i]=i;
        re[i]=0;
    }
}
int Find(int x)
{
    if(pre[x]==x)
        return x;
    else
    {
        int temp=Find(pre[x]);                     //先要从子节点向根节点搜索,进入最里面的节点,寻找根节点
        re[x]=(re[pre[x]]+re[x])%2;   //此处很重要,是通过根节点与父节点(爷与父)的关系和这个节点与父节点原来状态的关系(是否是同一集合的问题)求这个节点现在的状态,也就是与将要连到根节点的关系(异或关系)
        return pre[x]=temp;                     //关系确定后再将路径压缩,子节点连入根节点(此时rel的值也缺定了)
    }           //顺序不可变,先更新re值,再连接点根节点上
}
void Join(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    pre[fx]=fy;                                  //直接结合只要是D指令,将必定不是同一集合,将树中的两个数分在不同集合
    if (re[y]==0)                              //且fx的父节点不是y而是y的根节点
        re[fx]=1-re[x];                    //如果fy将成为fx的父节点,如果他将通过该x与原来根节点的关系re[x]和现在新建成的根节点y和x(此时属于不同集合)得到原来根节点的值,也就是与现在根节点fy的关系(同时,他正确的情况下可以通过find将所以的值rel正确)
    else re[fx]=re[x];                  //此时 的情况属于x和y了不同集合,y和fy不同集合(fy也就是其根节点也是父节点)那么x和fy同一集合,根节点的re一定为0,那么re[X]与原根节点是否同一集合就是re[fx]
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        for(int i=1; i<=m; i++)
        {
            getchar();
            scanf(" %c %d %d",&c,&a,&b);
            if(c=='A')
            {
                if(same(a,b)!=1)         //这里面包含Find函数,所以re值正确,为后面判断做铺垫,做不在这个树内则无法判断
                    printf("Not sure yet.\n");
                else if(re[a]==re[b])  //这个表示与根节点的相同与否,也就是否是一个集合
                    printf("In the same gang.\n");
                else
                    printf("In different gangs.\n");
            }
            else
                Join(a,b);
        }
    }
    return 0;
}