现在的位置: 首页 > 综合 > 正文

POJ 1182 食物链 (种类并查集)

2014年01月04日 ⁄ 综合 ⁄ 共 1770字 ⁄ 字号 评论关闭
/**
*   种类并查集:
*   这题数据天坑呐。。 必须单组才能AC,用多组数据输入就WA。
*       就下程序而言: Node中的fa表示他爸,re表示他和他爸的关系(吃他爸:1,被他爸吃:2,还是同类:0)。
*   当该节点没爸的时候,也就是本身就是根节点的时候,re就是0(和自己同类),他所有儿子的re都是和他的关系。
*       这题难点主要就是找规律且要在合并两个集合的时候怎么改变关系re;
*   我的思路是,把a对b的关系用向量来表示,如:a->b 如果re是1 就是a吃b, 2就是a被b吃, 0同类
*   这样当a->b, b->c的时候可以找到 a->c的关系:num[pre].re = (num[fa].re + num[pre].re) % 3
*   有了这个关系在合并集合的时候就可以用了。 有的时候还要转换,就是比如:
*   根据之前的规律可以求出a->c, 可是我需要的是c->a 其实,就可以一一考虑情况
*   当a->c 的re = 1的时候,改2,2的时候改1,0的时候不变。。这样就转换为c->a的关系了
*       具体的合并过程:  什么时候要合并?
*   当给两个节点x, y的时候find(x) != find(y) 也就是不同爸,说明该话是真,但是需要合并。
*   函数Union(int d, int x, int y) 中 d为y->x的关系re
*   Union就是把x所处集合作为y的儿子来合并集合的。
*   这个时候已知的有:  y->x  x->find(x), 就可以求 y->find(x) 再求 find(x)->y 最后把这个re给num[find(x)].re
*   当然还有很多细节,比如题目给的d只有1,2要把1转为0,还有父亲的变化,也就是并查集的维护等等。
*/

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#define INF 0x7fffffff
#define MAXS 50001
#define LL long long
using namespace std;
int n, k;
struct Node{
    int fa, re;
} num[MAXS];

void init() {
    for(int i = 1; i <= n; i ++) {
        num[i].fa = i; num[i].re = 0;
    }
}

int find(int pre) {
    int fa = num[pre].fa;
    if(num[fa].fa == fa) {
        return num[pre].fa;
    } else {
        num[pre].fa = find(num[pre].fa);
        num[pre].re = (num[fa].re + num[pre].re) % 3;
        return num[pre].fa;
    }
}

void Union(int x, int y, int d) {
    int td, xfa;
    xfa = find(x);  num[xfa].fa = y;
    td = (num[x].re + d) % 3;
    if(td == 1)       num[xfa].re = 2;
    else if(td == 2)  num[xfa].re = 1;
    else              num[xfa].re = 0;
}

bool judge(int d, int x, int y) {
    if(x > n || y > n) return false;
    if(d == 2 && x == y) return false;
    if(d == 0) {
        if(find(x) == find(y)) {
            if(num[x].re != num[y].re) return false;
            else    return true;
        } else {
            Union(x, y, d);
            return true;
        }
    } else if(d == 2) {
        if(find(x) == find(y)) {
            if(num[x].re == 0 && num[y].re == 2 ||
               num[x].re == 1 && num[y].re == 0 ||
               num[x].re == 2 && num[y].re == 1)
                  return true;
            else  return false;
        } else {
            Union(x, y, d);
            return true;
        }
    }
}

int main()
{
    scanf("%d%d", &n, &k);
        int X, Y, d, cnt = 0;
        init();
        for(int i = 0; i < k; i ++) {
            scanf("%d%d%d", &d, &X, &Y);
            if(d == 1) d = 0;
            if(!judge(d, X, Y)) cnt ++;
        }
        printf("%d\n", cnt);
    return 0;
}

抱歉!评论已关闭.