用一个经典的种类并查集做例子,食物链那一题

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=50050;
int n,k,q,a,b;
int p[N];
//num数组 0表示和父节点同类 1表示被父节点吃,2表示吃父节点
//所有取模前提条件都是A吃B B吃C C吃A 长度为3的环
int num[N];
int find(int x){
   
    if(x==p[x]){
   
        return x;
    }
    //要先保存这个父节点,否则下面压缩路径后p[x]会变
    int fa=p[x];
    //路径压缩
    p[x]=find(p[x]);
    //关系更新
    num[x]=(num[x]+num[fa])%3;
    return p[x];
}
//真话返回true
bool join(int a,int b,int q){
   
    int fa = find(a);
    int fb = find(b);
    //同一个根节点
    if(fa == fb){
   
        //q是操作 1表示同类 2表示a吃b
        //q如果是1 那么a和b就是同类,num值不同即为假话
        //q如果是2 那么a吃b,那么为了使得满足三角关系,b必须吃父节点,也就是num[b]为2,num[a]为1,或者num[b]为0,num[a]为2,即a吃b,a吃fa/fb,b和父节点同类,所以加了个取模
        //这里q-1是因为我们定义0同类 1被吃 比题目输入少1
        if(num[b] != (num[a] + (q - 1)) % 3){
   
            return false;
        }
        else{
   
            return true;
        }
    }
    else{
   
        //b合并到a,所以要更新num[b]
        p[fb] = fa;
        //这个公式可以通过向量的关系图推出(见上图)
        num[fb] = (3-num[b]+num[a]+(q-1))%3;
        //没有关系连通,所以肯定不会矛盾
        return true;
    }
}
int main(void){
   
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
   
        p[i]=i;
    }
    int cnt=0;
    while(k--){
   
        scanf("%d%d%d",&q,&a,&b);
        //特殊情况
        if(a>n || b>n ||(q==2 && a==b)){
   
            cnt++;
        }
        else{
   
            if(!join(a,b,q)){
   
                cnt++;
            }
        }
    }
    printf("%d\n",cnt);
    return 0;
}