食物链【并查集】

​NOI2001​​​、​​POJ1182​​​、​​ACwing240​

#include<bits/stdc++.h>
using namespace std;

#define
const int N = 50000;

int father[N + 5],dis[N + 5]; //分别记录祖先、距离(假定0>2>1>0,即题目中的ABC关系)
int n,k,ans,d,x,y,fx,fy;

int find_f(int x){ //找到x的祖先并且进行路径压缩和dis数组的更新
if(father[x] == x)
return x;
int root = find_f(father[x]); //找到根节点
dis[x] = (dis[x] + dis[father[x]]) % 3; //递归的上一层中dis[father[x]]更新了,因此要接着跟新dis[x]
return father[x] = root; //路径压缩
}

void solve(){
for(int i = 1;i <= N;++i) //初始化
father[i] = i;
cin>>n>>k;
while(k--){
cin>>d>>x>>y;
if(x > n || y > n){ //如果x、y大于n那么是假话
++ans;
continue;
}
fx = find_f(x);fy = find_f(y); //找到x和y的根节点
if(d == 1){ //如果d=1说话这句话代标x和y是同类
if(fx == fy){
if(dis[x] != dis[y])//如果x和y在同一个集合(已经被处理过了),并且他们的等级不同,
//说明在这句话之前的话证明了x和y不是同类,说明这句话是假话
ans += 1;
}
else{ //否则合并
father[fx] = fy; //将fx接到fy上
dis[fx] = (dis[y] - dis[x] + 3) % 3;
//合并过后需要对dis[fx]进行更新,(dis[x] + 新dis[fx])%3=dis[y]
//也就是说合并过后要让x和y的等级相同,推导:新dis[fx] = (dis[y] - dis[x] + 3)%3
}
}else{ //d=2说明这句话代表x吃y
if(x == y){ //x和y相同说明是x吃x这种情况,是假话
++ans;
continue;
}
if(fx == fy){ //如果已经合并过了
if(dis[x] != (dis[y] + 1) % 3){
//如果满足x吃y那么:dis[x] = (dis[y] + 1) % 3
//如果不满足这种情况,那么说明这句话是假话
++ans;
continue;
}
}
else{ //否则合并
father[fx] = fy; //将fx连接到fy
dis[fx] = (dis[y] - dis[x] + 1 + 3) % 3;
//连接后要满足:(dis[x] + 新dis[fx])%3=(dis[y] + 1)%3
//推导:新dis[fx] = (dis[y] + 1 - dis[x] + 3)%3
}
}
}
cout<<ans;
}

signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
// int tt;
// cin>>tt;
// while(tt--)
solve();
return 0;
}