POJ 1182 食物链

  食物链是一道经典的并查集题,这道题难在怎么确定两个动物之间的关系。

看了罗德安大神的分析,有种豁然开朗的感觉。

问题分析:

    我们把已经确立了关系的动物分到一个群落里,对于每一个提到的两个数字编号, 看它们是

否属于同一个群落,如果是的那么根据它们已有的关系,我们作出相应的判断 即可;若它们属于

不同的群落,那么根据互相的关系将两个群落合并;若恰有一个没有 加入到任意一个群落之中,

那么将这一个并入另一个的群中即可;若两个都未加入任意 群中,那么我们新建一个群即可。

基本思路:

这一题的难点在于记录动物之间的关系,即ABC三种动物是循环被吃的,我们用 0,1,2表示,记录

在rank[i]中,如果i吃j,则让rank[i]=(rank[j]+1)%3.为了便于处理, rank[i]表示第i个动物相对

于其父节点动物的差值,而不直接表示他本身的类别。这样 我们可以通过并查集自身的性质巧妙的

维护rank[]的值,所有根结点的rank[]值为0.

/*Accepted    512K    329MS    C++    1029B    2012-08-08 15:23:35*/

#include<stdio.h>

#include<string.h>

#include<stdlib.h>



const int MAXN = 50050;

int p[MAXN], rank[MAXN];

int n, k, op, ans;



int find_set(int x)

{

    int t = p[x];

    if(p[x] != x)

        p[x] = find_set(p[x]);

    rank[x] = (rank[x] + rank[t]) % 3;

    return p[x];

}



void union_set(int x, int y, int op)

{

    int nx = find_set(x);

    int ny = find_set(y);

    p[nx] = ny;

    rank[nx] = (rank[y] - rank[x] + 3 + op - 1) % 3;

}



int judge(int x, int y, int op)

{

    if(find_set(x) == find_set(y))

    {

        if(rank[x] != (rank[y] + op - 1) % 3 )

            return 1;

    }

    return 0;

}



int main()

{

    int i, x, y;

    scanf("%d%d", &n, &k);

    ans = 0;

    for(i = 1; i <= n; i ++)

    {

        p[i] = i;

        rank[i] = 0;

    }

    while(k --)

    {

        scanf("%d%d%d", &op, &x, &y);

        if(x > n || y > n || judge(x, y, op))

            ++ ans;

        else

            union_set(x, y, op);

    }

    printf("%d\n", ans);

    return 0;

}

 

 

 

你可能感兴趣的:(poj)