食物链
带权并查集,通过维护结点与根节点的距离 (代表相对关系),查询集合内点的关系;
由于题目描述只有三个物种,并且构成一个循环食物链,那么当知道了a与c 和 b与c 的关系后,就能推断出a与b的关系;在合并集合时,比如x吃y,那就让y做x的父节点,由此 若x->y->z->w,代表x吃y、y吃z、z吃w,w是根,此时能推断出 x 必然与 w 是同类(三个一循环)。
#include<iostream>
using namespace std;
int p[50010];
int d[50010]; //初始时,d[x] 都为0 代表 x 到其父节点p[x] 的距离
int find(int x){
if(p[x] !=x) {
int r = find(p[x]);
d[x] += d[p[x]]; //在 find() 结束后 d[x] 已经更新成 x 到根节点的距离;
p[x] = r;
}
return p[x]; //路径压缩
}
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) p[i] = i;
int t,x,y;
int ans=0;
while(k--){
cin>>t>>x>>y;
if(x>n||y>n) ans++;
else {
int px = find(x),py = find(y);
if(t==1){
if(px==py&&(d[x]-d[y])%3!=0) ans++; //不是同类
else if(px!=py){
p[px] = py;
d[px] = d[y] - d[x]; //保证 d[x] == d[y] (同类) 注意这里的d[x]含义的不同;
}
}
else{
if(px==py&&(d[x]-d[y]-1)%3!=0) ans++; //x不吃y
else if(px!=py) {
p[px] = py;
d[px] = d[y] -d[x] +1; // 保证d[x] = d[y] +1
}
}
}
}
cout<<ans<<endl;
return 0;
}