#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int maxn = 100000*3 + 100;
int par[maxn];
int Rank[maxn];
int N, K;
int T[maxn], X[maxn], Y[maxn];
//初始化n个元素
void init(int n)
{
for (int i = 0; i < n; i++)
{
par[i] = i;
Rank[i] = 0;
}
}
//查询树的根
int Find(int x)
{
if (par[x] == x)
{
return x;
}
else
{
return par[x] = Find(par[x]);
}
}
//合并x和y所述的集合
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 input()
{
scanf("%d%d", &N, &K);
for (int i = 0; i < K; i++)
{
scanf("%d%d%d", &T[i], &X[i], &Y[i]);
}
}
void solve()
{
input();
//初始化并查集
//元素x, x + N, x + 2*N 分别代表 x-A, y-B, x-C
init(N * 3);
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 || x >= N || y < 0 || y >= N)
{
ans++;
continue;
}
if (t == 1)
{
//"x和y属于同一类"的信息
if (same(x, y + N) || same(x, y + 2*N))
{
ans++;
}
else
{
//同属A,或B,或C类
unite(x, y);
unite(x + N, y + N);
unite(x + 2*N, y + 2*N);
}
}
else {
//"x吃y"的信息错,同为一类,或者隔了1类
if (same(x, y) || same(x, y + 2*N)) {
ans++;
}
else {
unite(x, y + N); // A -> B
unite(x + N, y + 2 * N); // B -> C
unite(x + 2 * N, y); // C -> A
}
}
}
printf("%d\n", ans);
}
int main()
{
solve();
return 0;
}