#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define mp make_pair
#define CHECK(x) if(!(x)) return false;
#define CHECKRET(x, y) if(!(x)) return (y);
#define SKIP(x) if((x)) continue;
typedef pair<int, int> pii;
#ifdef INFOARENA
#define ProblemName "cutii"
#else
#define ProblemName "fis"
#endif
#define InFile ProblemName ".in"
#define OuFile ProblemName ".out"
const int MAXBUF = 30000000;
char parseBuf[MAXBUF];
char *head;
bool isDigit[255];
void parseInit() {
int a = fread(parseBuf, 1, MAXBUF, stdin);
parseBuf[a] = 0;
head = parseBuf;
memset(isDigit, 0, sizeof isDigit);
for (int i = '0'; i <= '9'; ++i)
isDigit[i] = true;
}
int nextInt() {
int ans = 0;
for (; !isDigit[(int)*head]; ++head);
for (; isDigit[(int)*head]; ++head)
ans = ans * 10 + (*head) - '0';
return ans;
}
struct node {
int key;
int height;
node* left;
node* right;
int size, val;
int maxelem;
void set(int k, int v) {
key = k;
left = right = NULL;
height = 1;
size = 1;
val = v;
maxelem = v;
}
node(int k, int v) {
set(k, v);
}
};
deque<node> nodeCache;
stack<node*> recycleBin;
node *fetchNode(int k, int val) {
if (!recycleBin.empty()) {
node *t = recycleBin.top();
recycleBin.pop();
t->set(k, val);
return t;
}
nodeCache.push_back(node(k, val));
return &nodeCache.back();
}
void destroyNode(node *n) {
if (n == NULL) return;
recycleBin.push(n);
destroyNode(n->left);
destroyNode(n->right);
}
inline int height(node* p) {
return p ? p->height : 0;
}
inline int size(node *p) {
return p ? p->size : 0;
}
inline int bfactor(node* p) {
return height(p->right) - height(p->left);
}
inline int maxelem(node *p) {
return p ? p->maxelem : 0;
}
void fixheight(node* p) {
int hl = height(p->left);
int hr = height(p->right);
p->height = (hl>hr ? hl : hr) + 1;
p->size = size(p->left) + size(p->right) + 1;
p->maxelem = max(p->val, max(maxelem(p->left), maxelem(p->right)));
}
node* rotateright(node* p) {
node* q = p->left;
p->left = q->right;
q->right = p;
fixheight(p);
fixheight(q);
return q;
}
node* rotateleft(node* q) {
node* p = q->right;
q->right = p->left;
p->left = q;
fixheight(q);
fixheight(p);
return p;
}
node* balance(node* p) {
fixheight(p);
if (bfactor(p) == 2) {
if (bfactor(p->right) < 0)
p->right = rotateright(p->right);
return rotateleft(p);
}
if (bfactor(p) == -2) {
if (bfactor(p->left) > 0)
p->left = rotateleft(p->left);
return rotateright(p);
}
return p;
}
node* insert(node* p, int k, int val) {
if (!p) return fetchNode(k, val);
if (k < p->key)
p->left = insert(p->left, k, val);
else
p->right = insert(p->right, k, val);
return balance(p);
}
int tquery(node *root, int y) {
CHECKRET(root != NULL, 0);
if (root->key > y) return tquery(root->left, y);
int best = 0;
best = max(best, maxelem(root->left));
best = max(best, root->val);
best = max(best, tquery(root->right, y));
return best;
}
const int MAXN = 3510;
pair<int, pii> v[MAXN];
node *AIB[MAXN];
int arbSz;
inline int zeros(int x) {
return ((x ^ (x - 1)) & x);
}
void update(int x, int y, int val) {
for (int i = x; i <= arbSz; i += zeros(i))
AIB[i] = insert(AIB[i], y, val);
}
int query(int x, int y) {
int ret = 0;
for (int i = x; i > 0; i -= zeros(i))
ret = max(ret, tquery(AIB[i], y));
return ret;
}
int main() {
assert(freopen(InFile, "r", stdin));
assert(freopen(OuFile, "w", stdout));
parseInit();
int N, T;
N = nextInt();
T = nextInt();
arbSz = N;
while (T--) {
for (int i = 1; i <= N; ++i)
destroyNode(AIB[i]);
for (int i = 1; i <= N; ++i) {
v[i].first = nextInt();
v[i].second.first = nextInt();
v[i].second.second = nextInt();
}
sort(v + 1, v + N + 1);
int best = 0;
for (int i = 1; i <= N; ++i) {
int rez = query(v[i].second.first - 1, v[i].second.second - 1);
++rez;
best = max(best, rez);
update(v[i].second.first, v[i].second.second, rez);
}
printf("%d\n", best);
}
return 0;
}