poj1182食物链_带权并查集

题目大意:有三类动物,A, B, C。A吃B,B吃C,C吃A。现在有n个动物(属于以上三类),
m个陈述:d u v, d==1表示u和v为同类,d==2表示u吃v。
三种情况当前的话为假话
(1)当前的话与前面的话冲突
(2)当前的话中u或者v大于n
(3)当前的话表示u吃u
问假话的数量

fa[i]表示i的父亲的编号
relation[i]表示i与其父亲的关系:0表示相同,1表示被父亲吃,2表示吃父亲

!!!关系的传递:类比向量:若(a, b)表示a对b的关系,则
(1)(a, b) = (a, c) + (c, b);
(2)(a, b) = 3 - (b, a);或者(a, b)+(b, a)=3;

#include <cstdio>

using namespace std;

const int maxn = 50010;
int fa[maxn], relation[maxn];

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

int findf(int x)
{
    int tmp;
    if(x == fa[x]) return x;
    tmp = fa[x];
    fa[x] = findf(fa[x]);
    relation[x] = (relation[x] + relation[tmp]) % 3;//更新x和根节点的关系
    return fa[x];
}

int main()
{
    int i, n, m, d, u, v, ans = 0;
    int f1, f2;
    scanf("%d %d", &n, &m);
    init(n);
    for(i = 1; i <= m; ++i)
    {
        scanf("%d %d %d", &d, &u, &v);
        if(u > n || v > n)
        {
            ++ans;
            continue;
        }
        if(d == 2 && u == v)
        {
            ++ans;
            continue;
        }
        f1 = findf(u);
        f2 = findf(v);
        if(f1 != f2)
        {//合并两个集合
            fa[f2] = f1;
            relation[f2] = (3-relation[v] + (d-1) + relation[u]) % 3;//更新v的父节点和u的父节点的关系(向量法)
            continue;
        }
        if(d == 1 && relation[u] != relation[v])
        {
            ++ans;
            continue;
        }
        if(d == 2 && (3-relation[u] + relation[v]) % 3 != d-1)
        {
            ++ans;
            continue;
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jay__bryant/article/details/80377619