食物链(最全的代码+解析)
#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; }