#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int n, m;
int dp[N], uu[N], vv[N], ww[N], sz[N], orgw[N];
map<int, int> idw[N];
vector<int> vals[N];
vector<int> ft[N], a[N];
void update(int x, int val, int id) {
  if (a[id][x] >= val) return;
  a[id][x] = val;
  for (; x <= sz[id]; x += x & -x) ft[id][x] = max(ft[id][x], val);
}
void build() {
  for (int i = 0; i < N; i++) ft[i] = vector<int>(sz[i] + 2, 0), a[i] = ft[i];
}
int query(int x, int id) {
  int ans = 0;
  for (; x > 0; x -= x & -x) ans = max(ans, ft[id][x]);
  return ans;
}
void compress() {
  for (int i = 0; i < N; i++) {
    int cnt = 0;
    for (auto &pr : idw[i]) pr.second = ++cnt;
  }
  for (int i = 0; i < m; i++) ww[i] = idw[vv[i]][ww[i]];
  for (int i = 0; i < N; i++)
    for (auto pr : idw[i]) {
      vals[i].push_back(pr.first);
    }
}
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    cin >> uu[i] >> vv[i] >> ww[i];
    orgw[i] = ww[i];
    sz[vv[i]]++;
    idw[vv[i]][ww[i]] = 0;
  }
  compress();
  build();
  int ans = 0;
  for (int i = 0; i < m; i++) {
    int order = lower_bound(vals[uu[i]].begin(), vals[uu[i]].end(), orgw[i]) -
                vals[uu[i]].begin();
    int cur = query(order, uu[i]) + 1;
    ans = max(cur, ans);
    update(ww[i], cur, vv[i]);
  }
  cout << ans << endl;
}
