题意:
一共有三类动物,A,B,C,有这么一种关系A吃B,B吃C,C吃A,现在给你N个动物,M个关系,问有多少个关系是错误的。输入的格式为 op x y,当op = 1时表示 x 和 y 是同类,当op = 2时表示 x 吃 y。
分析:
这个题很明显的就是带权并查集了,边的权重代表的是到这个集合的根节点的距离,当x到根节点的距离比y少1的时候就代表x吃y,现在有d数组表示点到其父节点的距离(时隔两年的更新: 之前我理解的是到其根节点的距离,其实是不正确的,因为路径压缩后其父节点就变成根节点了,所以说可以直接说成是根节点,但是其实是父节点,然后更新的时候是d[x] += d[fa[x]],假如他和他父亲隔着d[x],然后他父亲和他父亲的父亲隔着d[fa[x]],那么他到他爷爷不就是d[x]+d[fa[x]]吗,只不过因为路径压缩的原因把这一系列的思考浓缩成了一个语句,当时说实话没太明白怎么搞的,现在重新审视一下这道题,确实有更深的理解和思考了)
,若(d[x]-d[y]+3)%3 = 0表示x和y是同类,若(d[x]-d[y]+3)%3=1表示y吃x,现在先看一张图片吧
现在如果是x吃y的关系,那么最后的结果就是(dx - dy + 3) % 3 = 1,那么合并fx和fy的话就是:
?+dx+1=dy,那么? = dy-dx-1,所以说更新的时候就是(dy-dx-1+3)%3,集合合并的前提是x和y不在同一个集合内,那么就能结合,如果x和y在一个集合内的话就要判断一下dy和dx的关系了,下面请看代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cstdlib>
#include<climits>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef unsigned long long ull;
const int mod = 1e9+7;
const int N = 50010;
int fa[N],d[N],n,m;
int find(int x){
// if(x != fa[x]){
// int t = find(fa[x]);
// d[x] = (d[x] + d[fa[x]]) % 3;
// fa[x] = t;
// }
if(x!=fa[x])
{
int t=fa[x];
fa[x]=find(fa[x]);
d[x]=(d[x]+d[t])%3;
}
return fa[x];
}
int main(){
cin>>n>>m;
int ans = 0;
for(int i=1;i<=n;i++) fa[i] = i;
for(int i=1;i<=m;i++){
int op,x,y;
cin>>op>>x>>y;
if((x > n || y > n) || (x == y && op == 2)){
ans++;
continue;
}
int c = (op==2)?1:0;
int pa = find(x),pb = find(y);
if(pa == pb){
if(op == 1 && d[x] != d[y]) ans++;
if(op == 2 && (d[x] - d[y] + 3) % 3 != 2) ans++;
}
else{
fa[pa] = pb;
d[pa] = (d[y] - d[x] - c + 3) % 3;
}
}
cout<<ans<<endl;
return 0;
}