uva10158 - War 并查集判断同伙的两种方法

Problem B: War

 

 

    A war is being lead between two countries, A and B. As a loyal citizen of C, you decide to help your country’s espionage by attending the peace-talks taking place these days (incognito, of course). There are n people at the talks (not including you), but you do not know which person belongs to which country. You can see people talking to each other, and through observing their behaviour during their occasional one-to-one conversations, you can guess if they are friends or enemies. In fact what your country would need to know is whether certain pairs of people are from the same country, or they are enemies. You may receive such questions from C’s government even during the peace-talks, and you have to give replies on the basis of your observations so far. Fortunately nobody talks to you, as nobody pays attention to your humble appearance.

 

  Abstract

    Now, more formally, consider a black box with the following operations:

               setFriends(x, y)     shows that x and y are from the same country

               setEnemies(x, y)   shows that x and y are from different countries

               areFriends(x, y)     returns true if you are sure that x and y are friends

               areEnemies(x, y)   returns true if you are sure that x and y are enemies

    The first two operations should signal an error if they contradict with your former knowledge. The two relations ‘friends’ (denoted by ~) and ‘enemies’ (denoted by *) have the following properties:

              ~ is an equivalence relation, i.e.

1.      If x ~ y and y ~ z then x ~ z  (The friends of my friends are my friends as well.)

2.      If x ~ y then y ~ x                  (Friendship is mutual.)

3.      x ~ x                                       (Everyone is a friend of himself.)

              * is symmetric and irreflexive

4.      If x * y then y * x                  (Hatred is mutual.)

5.      Not x * x                                (Nobody is an enemy of himself.)

              Also

6.      If x * y and y * z then x ~ z   (A common enemy makes two people friends.)

7.      If x ~ y and y * z then x * z   (An enemy of a friend is an enemy.)

    Operations setFriends(x, y) and setEnemies(x, y) must preserve these properties.

 

 

  Input

     The first line contains a single integer, n, the number of people.

     Each of the following lines contains a triple of integers, c x y, where c is the code of the operation:

            c = 1, setFriends

            c = 2, setEnemies

            c = 3, areFriends

            c = 4, areEnemies

     and x and y are its parameters, which are integers in the range [0, n), identifying two (different) people. The last line contains 0 0 0.

    All integers in the input file are separated by at least one space or line break.

 

  Output

     For every ‘areFriends’ and ‘areEnemies’ operation write 0 (meaning no) or 1 (meaning yes) to the output. Also for every ‘setFriends’ or ‘setEnemies’ operation which contradicts with previous knowledge, output a –1 to the output ; note that such an operation should produce no other effect and execution should continue. A successful ‘setFriends’ or ‘setEnemies’ gives no output.

    All integers in the output file must be separated by at least one space or line break.

 

  Constraints

    n < 10000, the number of operations is unconstrained.

 

 

  Sample Input

            10

            1 0 1

            1 1 2

            2 0 5

            3 0 2

            3 8 9

            4 1 5

            4 1 2

            4 8 9

            1 8 9

            1 5 2

            3 5 2

            0 0 0

 

  Sample Output

            1

            0

            1

            0

            0

            -1

            0


  以前做过类似的,也写过一篇博客,用flag标记某节点跟他父结点是不是一伙。今天在网上搜了一下,还有一种超级简单的神奇方法。

  如果a是一个国家,那么我们可以设与他对立的一个国家a+N。并且一个集合里的全是一伙的。如果a和b对立,那么就可以把a和b+N并到一个集合里(a和b的对立说明a和b+N一伙)。

  我以前也想过一个集合代表一伙,但是不知道如果对立怎么弄,今天算是知道了。。合并a,b的时候把a+N,b+N合并,合并a,b+N的时候把a+N,b合并。这样如果再出现个c跟a的关系,也可以轻松的判断c和b的关系了。

  最后判断是不是一伙的时候就看p[a]是不是等于p[b],判断对立的时候就看p[a]是不是等于p[b+N]。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define INF 0x3f3f3f3f
#define MAXN 10010
#define MAXM 5010
#define eps 1e-9
using namespace std;
int N,p[2*MAXN];
int find(int x){
    return p[x]==x?x:p[x]=find(p[x]);
}
int main(){
    freopen("in.txt","r",stdin);
    while(scanf("%d",&N)!=EOF){
        for(int i=0;i<2*N;i++) p[i]=i;
        int type,a,b,x,y,m,n;
        while(scanf("%d%d%d",&type,&a,&b),type||a||b){
            x=find(a);
            y=find(b);
            m=find(a+N);
            n=find(b+N);
            if(type==1){
                if(x==n) printf("-1\n");
                else{
                    p[x]=y;
                    p[m]=n;
                }
            }
            if(type==2){
                if(x==y) printf("-1\n");
                else{
                    p[x]=n;
                    p[y]=m;
                }
            }
            if(type==3){
                if(x==y) printf("1\n");
                else printf("0\n");
            }
            if(type==4){
                if(x==n) printf("1\n");
                else printf("0\n");
            }
        }
    }
    return 0;
}

  还有一种方法,就是原来做的那个方法,在一个集合里只说明他们有一伙或者敌对的关系,具体是什么关系要通过flag判断。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define INF 0x3f3f3f3f
#define MAXN 10010
#define MAXM 5010
#define eps 1e-9
using namespace std;
int N,p[MAXN],flag[MAXN];
int find(int x){
    if(x==p[x]) return x;
    int t=p[x];
    p[x]=find(p[x]);
    flag[x]=(flag[t]+flag[x])%2;
    return p[x];
}
int main(){
    freopen("in.txt","r",stdin);
    while(scanf("%d",&N)!=EOF){
        memset(flag,0,sizeof(flag));
        for(int i=0;i<N;i++) p[i]=i;
        int type,a,b,x,y;
        while(scanf("%d%d%d",&type,&a,&b),type||a||b){
            x=find(a);
            y=find(b);
            if(type==1){
                if(x==y&&flag[a]!=flag[b]) printf("-1\n");
                else if(x!=y){
                    p[x]=y;
                    flag[x]=(flag[a]+flag[b])%2;
                }
            }
            if(type==2){
                if(x==y&&flag[a]==flag[b]) printf("-1\n");
                else if(x!=y){
                    p[x]=y;
                    flag[x]=(flag[a]+flag[b]+1)%2;
                }
            }
            if(type==3){
                if(x==y&&flag[a]==flag[b]) printf("1\n");
                else printf("0\n");
            }
            if(type==4){
                if(x==y&&flag[a]!=flag[b]) printf("1\n");
                else printf("0\n");
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值