题意:
A,B,C三种动物,A吃B, B吃C,C吃A。有n个动物,他们编号为1~n。
输入:第一行n,k,分别表示动物个数,给出k句话(有真有假)。接下来n行每行一句话,每句的格式为三个整数:d,x,y。x,y为动物编号,d为1时表示x,y是同类,d为2时表示x吃y。
说明:假话有三种: 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。
输出:假话的个数。
思路:
这个一个带权并查集,
tx 为 x 的父亲节点。
x -> tx 0 x and tx 同一类
x -> tx 1 x eat tx
x -> tx 2 tx eat x
我们开一个 dis 数组 来记录 x 与 tx 的关系。
dis[x] == 0 同一类
dis[x] == 1 x 吃 tx
dis[x] == 2 tx 吃 x
知道这样的我们怎么合并呢。 假如我们用 f[tx] = ty .
则 , tx -> x -> y - > ty == tx -> ty 这种就类似于向量。一定要注意方向。
所以,,dis[tx] = -dis[x] + dis[y] + (d - 1);
我们判断假话的情况。
x -> y, x->tx -> y 所以,,(dis[x] - dis[y] + 3) % 3 判断他们的结果是0 还是 1.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(x,v) memset(x,v,sizeof(x))
#define go(i,a,b) for (int i = a; i <= b; i++)
#define og(i,a,b) for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
int f[N],dis[N];
int find(int k){
if (f[k] == k) return k;
int temp = f[k];
f[k] = find(temp);
dis[k] = (dis[k] + dis[temp]) %3; //路径压缩。
return f[k];
}
int main(){
int n,m;
int Ans = 0;
scanf("%d%d",&n,&m);
go(i,1,n) f[i] = i,dis[i] = 0;
go(i,1,m){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if (x > n || y > n){Ans++; continue;}
if (op == 2 && x == y) {Ans++; continue;}
int xx,yy;
xx = find(x); yy = find(y);
if (xx != yy){
f[xx] = yy;
dis[xx] = (3 + dis[y] - dis[x] + op - 1)%3; //进行合并。
} else{
if (op == 1 && dis[x] != dis[y]){ //判断x 和 y 是不是同类。
Ans++; continue;
}
if (op == 2 && ((dis[x]-dis[y]+3)%3 != 1)){ //判断 x 是不是吃 y
Ans++;continue;
}
}
}
cout<<Ans<<endl;
return 0;
}