#include<bits/stdc++.h>
#pra\
gma GCC optimize(2)
using namespace std;
int f[100001],d[100001],p,x,y,n,m,ans=0;
int findfat(int x)
{
if(f[x]==x) return x;
int fa=f[x];
f[x]=findfat(f[x]);
d[x]=(d[x]+d[fa])%3;//食物链中关系分为0,1,2三种,其中0代表是同类,1代表被父节点吃,2代表吃父节点
//则每个父节点和自己的关系均为0(自己当然不能吃自己的啦)。
//它所连接的子节点(通过穷举法可进行证明)
/*
爷爷 父亲 儿子 爷爷和儿子关系
0 0 0(d[father]+d[当前])%3
0 1 1(d[father]+d[当前])%3
1 1 2
1 2 0
......
*//*PS:由于食物链是由三种(xx)形成的环,所以只需判断(儿子、父亲和爷爷的关系即可)*/
//由此便可以推出当前爷爷节点和儿子节点的关系
return f[x];
}
int main(){
cin>>n>>m;
for (int i=1; i<=n; i++) {f[i]=i; d[i]=0;}
for (int i=1; i<=m; i++)
{scanf("%d%d%d",&p,&x,&y);
if ((y>n || x>n) || p==2 && x==y) {ans++; continue;}//弱智判断。。。
int a=findfat(x),b=findfat(y);//由于每次都会find一次,所以每次并查集都可以更新
if (p==1)
{
if (a==b){//假设x和y已经在同一个集合里
if (d[x]!=d[y]) ans++;
//若两个对于相同的爷爷(也可以不同,但由于已经在同一个集合里了,这个问题可以忽略,因为本身食物链就是一个环状结构),如果他们的关系不同,则可以说明是假话
}
else
{
d[a]=(d[y]-d[x]+3)%3;//如果两者不在同一个集合里,则两者此时毫无关系,那么
//便可以将两者合并
//由于d[a],d[b]应该是0,所以d[y]就等于y节点在该环上所处的位置,d[x]也应该是x在该环上所处的位置
//同样有枚举法可以得到:
//PS:此时的父节点已经是最顶端的f[x]了
//儿子 父亲关于儿子的关系
//0 (3-0)%3=0
//1 (3-1)%3=2(说明父亲吃儿子。。。。)
//2 (3-2)%3=1(说明儿子吃父亲)
//所以(3-d[x])即在有x的集合中,最顶端父节点和儿子的关系
//不妨将其设为yw。
//那么d[a]就应该是y节点的儿子。。。。
//即.....完毕
f[a]=b;
}
}
else
{
if (a==b){
if (d[x]!=(d[y]+1)%3) ans++;
//如果在同一个并查集内,却不满足条件,则将其判断为假话
}
else
{
d[a]=(d[y]-d[x]+4)%3;
//同理
//将x的根节点接在y后面。。。。
//所以d[a]=d[y]+(3-d[x])——(根节点与x的关系)+1;
//结束。。。。。
f[a]=b;//将其合并
}
}}
cout<<ans<<endl;
return 0;
}
食物链详解
最新推荐文章于 2025-05-05 18:00:13 发布