食物链

题目
很经典的并查集题目
反集做法:开三倍的数组,分别是吃x的和x吃的。

#include <cstdio>
#include <iostream>
#include <cmath> 
using namespace std;
const int MAXN=200000;
int f1[MAXN];
int find(int x)//找爸+路径压缩
{
    if(f1[x]==x) return x;
    return f1[x]=find(f1[x]);
} 
int main()
{
    int n,m;
    int ans=0;

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

    for(int i=1;i<=3*n;i++)
     f1[i]=i;//三倍集合

    for(int i=1;i<=m;i++)
    {
        int x,y,c;

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

        if(x>n||y>n||(x==y&&c==2)) 
        {
            ans++;
            continue;//特判
        }

        int fx=find(x);
        int fy=find(y);

        if(c==1)
        {
           if(fx==fy)    continue;
            if(fx==find(y+n)||fx==find(y+2*n)||fy==find(x+2*n)||fy==find(x+n))
            {
                ans++;    continue;
            }        
        f1[find(x)]=find(y);//x的同类是y
        f1[find(x+n)]=find(y+n);//x+n的同类是y+n
        f1[find(x+2*n)]=find(y+2*n);//x+2n的同类是y+2n
        }
        else
        {
            if(fx==fy||fx==find(y+2*n)||fy==find(x+n))
            {
                ans++;    continue;
            }
            f1[fx]=find(y+n);//fx即使吃y的动物的同类
            f1[fy]=find(x+2*n);//fy即使被x吃动物的同类
            f1[find(x+n)]=find(y+2*n);//x吃y,y吃z,那么z就吃x0.
        }
    }

    printf("%d",ans);

    return 0;
}

带权并查集的方法
front数组是这个点到根节点的关系,因为只有三种动物且存在x吃y,y吃z那么z就吃x的关系,所以用0表示同类,1表示x吃y,2表示y吃z。
!(图片)注意有个关键就是当我们知道x与祖先x1的关系,y与祖先y1的关系,x与y的关系时,求x1与y1的关系时,使用矢量计算:
x1->x->y->y1 计算

所以front[x1]=(-front[x]+front[y]+c-1+3)%3//防止出负数

#include <cstdio>
#include <iostream>
#include <cmath> 
using namespace std;
int f1[999999],front[999999];
int find(int x)
{
    if(x==f1[x])
        return f1[x];
    int y=find(f1[x]);
        front[x]=(front[x]+front[f1[x]])%3;//计算与根节点的距离
    return f1[x]=y;
}
int main()
{
    int n,m;
    int ans=0;

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

    for(int i=1;i<=n;i++)
     f1[i]=i;

    for(int i=1;i<=m;i++)
    {
        int x,y,c;

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

        if(x>n||y>n||(x==y&&c==2)) //特判
        {
            ans++;
            continue;
        }

        int fx=find(x);
        int fy=find(y);

        if(fx==fy)//共同祖先,判断真话假话。
        {
            if((front[x]-front[y]+3)%3!=c-1) ans++;//出现矛盾假话++
        }
        else//不是共同祖先,默认真话,建立关系。
        {
            f1[fx]=fy;
            front[fx]=(-front[x]+c-1+front[y]+3)%3;//计算fx与fy的距离
        }
    }

    printf("%d",ans);

    return 0;
}
原文地址:https://www.cnblogs.com/ht008/p/6819833.html