没有大佬那么厉害的可以直接得出结论,只能把情况都列出来,其实一共只有18种情况,所以不妨头脑风暴一次,然后稍加整理(先根据d的值分类,再根据rank[a]的值进行分类)规律就显然了。
详细阐述下整理部分吧。
首先把d=1,2,这是两种情况,然后细分,例如,当d=1,把rank[a],rank[b],rank[x]的关系列出来,其实只有9种可能,所以一共是233=18种可能。
为了简化书写让a代替rank[a],b代替rank[b],x代替rank[x];
然后将x=0,1,2的情况列出:

在这里插入图片描述
然后你会发现其实当a,b一样时,x的值是与d值有关,例如,当d=1,x=1三组数据的a,b的值刚好与d=2,x=0三组数据的a,b的值有关,所以当d=2时,X2=(X1+2)%3,其中X2是当d=2,x的值,X1同理。所以我们只要讨论d=1的情况就好了。
然后我们再看当d=1的情况
当a=0,x=b;
当a=1,x=(b+2)%3
当a=2,x=(b+1)%3
然后把x的表达式与a联系起来,得到:x=(b+(3-a))%3;
所以X1=(b+(3-a))%3,X2=(b+(5-a))%3->(b+(2-a))%3
最后再把表达式同d联系起来:会惊奇地发现:x=(b+(4-d-a))%3
以上是我这个小菜鸡的表达式导出过程。
附代码:

#include<cstdio>
using namespace std;
//做了1个半小时做出来了
//感觉还是很不错的,对并查集了解更深刻了
//推理加找规律,很有意思
const int N = 5e4;
int father[N+5],rank[N+5];
//rank表示偏移量,rank[x]=y,偏移量为0同类,1为x被y吃,2为x吃y
int n;
void init(int n)
{
    for(int i = 1; i<=n; ++i)
    {
        father[i] = i;
        rank[i] = 0;
    }
}
int find(int x)
{
    if(x!=father[x])
    {
        int px = find(father[x]);
        rank[x] = (rank[x] + rank[father[x]]) % 3;
        father[x] = px;
    }
    return father[x];
}
int _union(int d,int a,int b)
{
    if(a>n||b>n)
        return 1;
    if(d==2&&a==b)
        return 1;
    int x = find(a);
    int y = find(b);
    if(x==y)
    {
        if(d==2&&(rank[a]+1)%3!=rank[b])
            return 1;
        if(d==1&&rank[a]!=rank[b])
            return 1;
    }
    else
    {
        father[x] = y;
        rank[x] = (rank[b]+(4-d)-rank[a])%3;
        //这个把结果都列出来一共 2*3*3 = 18种, 然后比较得到的规律
    }
    return 0;
}
int main()
{
    int k;
    int a,b,d;
    scanf("%d%d",&n,&k);
    init(n);
    int ans = 0;
    while(k--)
    {
        scanf("%d%d%d",&d,&a,&b);
        if(_union(d,a,b))
            ans++;
    }
    printf("%d\n",ans);
    return 0;
}