原题链接:http://poj.org/problem?id=1182
//用3*N的空间建立并查集,0~N-1,N~2N-1,2N~3N-1三个区间分别表示ABC三个种类
//同类:若xy同类,合并xy说明xy属于A类,当然也可能属于B,C类,此时合并x+N,y+N和x+2N,y+2N表示他们分别属于B和C类
//捕食:若x吃y, 合并x和y+N,表示A中x吃B中y,当然也可能Bx吃Cy,Cx吃Ay,此时用合并x+N,y+2N和y+2N来表示(不会再出现Bx吃Ay等,因为已经有Ax吃By,两种情况只能发生一种)
//并查集中合并在一组的所有元素代表情况同时发生或不发生 比如:A中i吃B中j,就表示如果i属于A,j一定属于B 或 如果j属于B,i一定属于A
//合并前先进行判断如果出现矛盾,就表示此语句是错误信息
//判断:1数据范围 2合并同类关系时,判断是否有捕食关系 3合并捕食关系,判断是否有同类关系和反捕食关系
#include <cstdio>
#include <cstring>
const int MAX_N = 50010;
const int MAX_K = 100010;
int N, K;
int T[MAX_K], X[MAX_K], Y[MAX_K]; //输入(T是信息的类型)
int par[MAX_N * 3];
int rank[MAX_N * 3];
void init(int n){ //并查集初始化n个元素
for(int i = 0;i < n;i ++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x){ //并查集查找
return par[x] == x ? x : par[x] = find(par[x]);
}
void unite(int x, int y){ //合并
x = find(x);
y = find(y);
if(x == y) return ;
if(rank[x] < rank[y]){
par[x] = y;
}else{
par[y] = x;
if(rank[x] == rank[y]) rank[x] ++;
}
}
bool same(int x, int y){ //根是否相同(同类或者捕食关系)
return find(x) == find(y);
}
void solve(){
init(N * 3);//初始化并查集,元素x,x+N,x+2*N分别代表x-A,x-B,x-C (x-A代表x属于种类A)
int ans = 0;
for(int i = 0;i < K;i ++){
int t = T[i];
int x = X[i] - 1, y = Y[i] - 1;//把输入变成0,...,N-1的范围
if(x < 0 || N <= x || y < 0 || N <= y){//不正确的编号
ans ++;
continue ;
}
if(t == 1){ //"x和y属于同一类"的信息
if(same(x, y + N) || same(y, x + N)){//Ax吃By或者Ay吃Bx
ans ++;
}else{
unite(x, y); //x与y同属A类,下同
unite(x + N, y + N); //x与y同属B类,下同
unite(x + 2 * N, y + 2 * N); //x与y同属C类,下同
}
}else{ //"x吃y"的信息
if(same(x, y) || same(y, x + N)){ //Ax与Ay同类,或者Ay吃Bx
ans ++;
}else{
unite(x, y + N); //Ax吃By
unite(x + N, y + 2 * N);//Bx吃Cy
unite(x + 2 * N, y); //Cx吃Ay
}
}
}
printf("%d\n", ans );
}
int main(){
scanf("%d%d", &N, &K);
for(int i = 0;i < K;i ++)
scanf("%d%d%d", &T[i], &X[i], &Y[i]);
solve();
return 0;
}