POJ 1703 Find them, Catch them

题目链接:http://poj.org/problem?id=1703

题意:有N个人,分别属于两个帮派。以两种形式给你M个信息①A a b 问a和b是否属于同一个帮派 ②D a b 告诉你a和b属于不同的帮派。(题中”每个帮派至少有一个人“这句话可无视,因为根据题目的输入形式,再考虑这句话的话这题实际上是无法做的)

分析:集合问题,并查集经典。

这里主要是解决集合的合并问题。因为每次给出的信息是两个人属于不同的集合,与一般给出的属于同一个集合的不同。那么我们也可以将他们合并,前提是要记录他们之间的关系。这里另开一个数组relation[],记录一个节点与其父亲节点的关系。relation[x]=0代表该节点与父亲节点属于同一帮派,relation[x]=1则代表他们不属于同一帮派。这样就可以处理并记录两个节点的信息。但问题又来了,当对两个节点x,y进行Union操作的时候,节点间的关系有如何处理呢?首先节点x,y的节点关系(relation[x],relation[y])并不需要变化,因为它们的父亲节没变。要变的只是x和y的父亲节点的relation。暂且把x,y的父亲节点叫做fx和fy吧。如果直接将x的父亲节点连到y的父亲节点,即father[fy]=father[fx],那么relation[y]应当怎么变化呢?这里可以列出x,y所有情况仔细分析一下:

                         


由以上可得 relation[fy]=(relation[x]+relation[y])^1。

但是当进行find操作时进行路径压缩时可能会改变某个节点的父亲节点,这样relation又要变化了。这种情况可以根据当前节点x的父亲节点fx与fx的父亲节点fxx的关系推出x与fxx的关系(推理同上,就是列出所有可能情况总结一下啦):relation[x]=(relation[x]+relation[fx])%2。

怎么样,不难吧?那再试试这题?http://poj.org/problem?id=1182(这题的详细分析见这里:http://blog.csdn.net/hrhacmer/article/details/9408571


Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <map>
#include <set>
#define eps 1e-8
#define LL long long
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(a))
using namespace std;

const int inf=0x3f3f3f3f;
const int maxn=100005;
int father[maxn],rela[maxn];
char s[5];
int n,m;

void make_set(){
    for(int i=1;i<=n;i++){
        father[i]=i;
        rela[i]=0;
    }
}

int find_set(int x){
    if(x!=father[x]){
        int tmp=father[x];
        father[x]=find_set(father[x]);
        rela[x]=(rela[x]+rela[tmp])%2;
    }
    return father[x];
}

void Union(int a,int b){
    int x=find_set(a);
    int y=find_set(b);
    if(x!=y) {
        father[y]=x;
        rela[y]=(rela[a]+rela[b])^1;
    }
}

int main()
{
    int T,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        make_set();
        while(m--){
            scanf("%s %d %d",s,&a,&b);
            if(s[0]=='A'){
                int fa=find_set(a);
                int fb=find_set(b);
                if(fa==fb){
                    if(rela[a]==rela[b]) printf("In the same gang.\n");
                    else printf("In different gangs.\n");
                }
                else printf("Not sure yet.\n");
            }
            else Union(a,b);
        }
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值