这道题是我学数据结构开始敲的第一道题,并查集,留作存目。这道题体现了一些问题,是我不知道的,也是我犯二了,竟然没想到过用类似于边权的东东来表示并查集中各个元素之间的关系的区别,不过还好了,现在转过来这个弯儿了。
这道题,所有的物种之间有三个关系,平等,吃与被吃,那么这三个关系可以分别用0,1,2来表示,如果遇到的两个生物从来没有处理过,或者说有一个没有处理过,那么这句话一定是跟前面的所有真话没有冲突的,如果这句话也没有违背剩下的两个条件,就可以把这个物种存进来,合并到一个集合中,并且关系给好好的设定一下,由于只有三个物种,而且三个物种之间的关系非常明确,我们就可以推导出来压缩路径的时候各个元素之间的关系的换算关系,至于关系之间的推导,则完完全全类似于平面向量,具体就是说,假如a吃b,那么a和b之间的向量就是b指向a,b吃c,b和c之间的向量就应该是c指向b,那么c和a是什么关系呢?想一下,如果a和b,b和c之间的向量相加,然后对总数取模,不是恰好凑成了a和c的关系,也就是c吃a?所以嘛,就叫向量偏移。如果遇到的两个物种全都处理过了,那么查找的时候必然是在同一个集合中,那就看它们的关系是否冲突就行了……如果它俩是同一个物种,那么它俩和根节点的关系一定就是一样一样的,如果不是同一个物种,换算公式啦……
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define M 50010
struct data{
int p, r;
}p[M];
int find(int x){
int temp;
if (x == p[x].p) return x;
temp = p[x].p;
p[x].p = find(temp);
p[x].r = (p[x].r + p[temp].r) % 3;
return p[x].p;
}
int main(){
int n, k;
int d, x, y;
int r1, r2;
int sum = 0;
cin >> n >> k;
for (int i = 1; i <= n; i++){
p[i].p = i;
p[i].r = 0;
}
for (int i = 0; i < k; i++){
scanf("%d%d%d", &d, &x, &y);
if (x > n || y > n){
sum++;
continue;
}
if (d == 2 && x == y){
sum++;
continue;
}
r1 = find(x); r2 = find(y);
if (r1 != r2){
p[r2].p = r1;
p[r2].r = (3 + (d - 1) + p[x].r - p[y].r) % 3;
}
else{
if (d == 1 && p[x].r != p[y].r){
sum++;
continue;
}
if (d == 2 && ((3 - p[x].r + p[y].r) % 3 != (d - 1))){
sum++;
continue;
}
}
}
cout << sum << endl;
return 0;
}