POJ 1182_食物链

题意:

三种动物A,B,C,A吃B,B吃C,C吃A,
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是”1 X Y”,表示X和Y是同类。
第二种说法是”2 X Y”,表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
求假话数

分析:

最初想到用三个并查集分别表示A,B,C,然后用数组表示之间的联系,可是操作起来很麻烦,过了样例也一直WA,后来看了《挑战程序设计竞赛》上的解法,用偏移量表示他们之间的关系,处理起来方便很多。即
X为A,X+N为B,X+2*N为C

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 500055;
int pa[maxn], _rank[maxn];
int _find(int x)
{
    if(pa[x]==x) return x;
    else return pa[x]=_find(pa[x]);
}
void unite(int x, int y)
{
    int rx = _find(x), ry = _find(y);
    if(rx==ry) return;
    if(_rank[rx]>_rank[ry]) pa[ry]=rx;
    else {
        pa[rx] = ry;
        if(_rank[rx]==_rank[ry]) _rank[ry]++;
    }
    return;
}
bool same(int x, int y)
{
    return _find(x)==_find(y);
}
int main (void)
{
    int n, k ;scanf("%d%d",&n,&k);
    for(int i = 1; i <= 3 * n; i++) pa[i] = i;
    int x, y, f, cnt = 0;
    int mod = 3 * n;
    for(int i = 0; i < k; i++){
        scanf("%d%d%d",&f,&x,&y);
        if(x>n||y>n) {cnt++;continue;}
        if(f==1){
            if(same(x,y+n)||same(x,y+2*n)) cnt++;
            else{
                unite(x,y);
                unite(x+n,y+n);
                unite(x+2*n,y+2*n);
            }
        }else {
            if(same(x,y)||same(x, y + 2*n)) cnt++;
            else {
                unite(x, y+n);
                unite(x+n,y+2*n);
                unite(x+2*n, y);
            }
        }
    }
    printf("%d\n",cnt);
}

你可能感兴趣的:(poj)