通俗易懂的博客 获益匪浅 点击打开链接
个人理解详见注释
初学种类并查集最好先做做模2循环的poj 2492
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=5e4+10;
int f[maxn],dis[maxn];
int n,q;
int getf(int p)
{
int res;
if(f[p]==p) return p;
res=getf(f[p]);
dis[p]=(dis[p]+dis[f[p]])%3;
f[p]=res;
return res;
}
int unite(int u,int v,int val)
{
int fu,fv;
fu=getf(u),fv=getf(v);
if(fu!=fv){
f[fv]=fu;//假设u掠食v 在合并时就将v所在集合的代表元素归于u所在集合的代表元素之下
dis[fv]=(dis[u]-dis[v]+val+3)%3;//先抛开取模循环关系 假设整个掠食系统就是一棵树 最高级消费者在树根 现在fu就是树根 知道fu比u高几层(dis[u]) fv比v高几层(dis[v]) 又知道u比v高几层(val) 那fu要比fv高几层就显而易见了 最后很自然的套上取模就好了
//还可能有的疑问就是 第一种情况:a吃b b吃c 在合并过程中b成为了树根 a与b c与b的距离都是1 那a与b的正确距离应该是dis[a]+dis[b]
//第二种情况:a吃b b吃c 在合并过程中a为树根 b为a孩子 c为b孩子 此时b与c的正确距离应该是dis[c]-dis[b]
//这两种情况不是互相矛盾吗?
//因为规定掠食者在上 被掠食者在下(抛开循环关系) 所以第一种被掠食者在上的情况根本不会出现
return 0;
}
else{
return (dis[v]-dis[u]+3)%3!=val;
}
}
int main()
{
int i,op,u,v,ans;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++){
f[i]=i,dis[i]=0;
}
ans=0;
while(q--){
scanf("%d%d%d",&op,&u,&v);
if(u>n||v>n) ans++;
else ans+=unite(u,v,op-1);
}
printf("%d\n",ans);
return 0;
}