#include <bits/stdc++.h>
using namespace std;
const int MAX = 1e5 + 5;
struct edge {
  int u, v, w;
  edge() {}
  edge(int u_, int v_, int w_) { u = u_, v = v_, w = w_; }
};
edge E[MAX];
vector<int> W[MAX];
vector<int> bst[MAX];
int query(int i, int now, int L, int R, int l, int r) {
  int mid = (L + R) >> 1;
  int lchld = now << 1;
  int rchld = lchld | 1;
  if (l > r) return 0;
  if (l <= L && R <= r) return bst[i][now];
  if (r <= mid) return query(i, lchld, L, mid, l, r);
  if (l > mid) return query(i, rchld, mid + 1, R, l, r);
  return max(query(i, lchld, L, mid, l, mid),
             query(i, rchld, mid + 1, R, mid + 1, r));
}
void update(int i, int now, int L, int R, int idx, int val) {
  int mid = (L + R) >> 1;
  int lchld = now << 1;
  int rchld = lchld | 1;
  if (L == R) {
    bst[i][now] = max(bst[i][now], val);
    return;
  }
  if (idx <= mid)
    update(i, lchld, L, mid, idx, val);
  else
    update(i, rchld, mid + 1, R, idx, val);
  bst[i][now] = max(bst[i][lchld], bst[i][rchld]);
}
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v, w;
    cin >> u >> v >> w;
    W[u].push_back(w);
    E[i] = edge(u, v, w);
  }
  for (int i = 1; i <= n; i++) {
    sort(W[i].begin(), W[i].end());
    W[i].erase(unique(W[i].begin(), W[i].end()), W[i].end());
    bst[i].resize((int)W[i].size() << 2);
  }
  int asholAns = 1;
  for (int i = m - 1; i >= 0; i--) {
    int u = E[i].u;
    int v = E[i].v;
    int w = E[i].w;
    int ans, idx, nn;
    idx = upper_bound(W[v].begin(), W[v].end(), w) - W[v].begin();
    nn = (int)W[v].size();
    ans = 1 + query(v, 1, 0, nn - 1, idx, nn - 1);
    asholAns = max(asholAns, ans);
    idx = lower_bound(W[u].begin(), W[u].end(), w) - W[u].begin();
    nn = (int)W[u].size();
    update(u, 1, 0, nn - 1, idx, ans);
  }
  cout << asholAns;
  return 0;
}
