#include <bits/stdc++.h>
#pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("O3", "unroll-loops", "omit-frame-pointer", "inline")
#pragma GCC option("arch=native", "tune=native", "no-zero-upper")
#pragma GCC target("avx")
using namespace std;
using ll = long long;
using ld = long double;
using ull = unsigned long long;
void shift() {
  cin.tie(0);
  cout.tie(0);
  ios_base::sync_with_stdio(0);
}
struct edge {
  int v, u, cost;
};
bool cmp(edge a, edge b) { return a.cost < b.cost; }
int dp[500200];
signed main() {
  shift();
  int n, m;
  cin >> n >> m;
  vector<edge> v;
  for (int i = 0; i < m; ++i) {
    int a, b, c;
    cin >> a >> b >> c;
    --a, --b;
    v.push_back({a, b, c});
  }
  sort(v.begin(), v.end(), cmp);
  int res = 0;
  vector<int> ddp(n);
  for (int i = 0; i < v.size(); ++i) {
    int j = i;
    vector<int> kek;
    while (j < v.size() && v[j].cost == v[i].cost) {
      ddp[v[j].u] = max(ddp[v[j].u], dp[v[j].v] + 1);
      kek.push_back(v[j].u);
      ++j;
    }
    for (int i = 0; i < kek.size(); ++i) {
      dp[kek[i]] = max(dp[kek[i]], ddp[kek[i]]);
      ddp[kek[i]] = 0;
    }
    i = j - 1;
  }
  for (int i = 0; i < n; ++i) res = max(res, dp[i]);
  cout << res;
  return 0;
}
