#include <bits/stdc++.h>
using namespace std;
const int infinity = 2000000999 / 2;
const long long int inff = 4000000000000000999;
template <class T>
T get() {
  T a;
  cin >> a;
  return a;
}
template <class T, class U>
ostream& operator<<(ostream& out, const pair<T, U>& par) {
  out << "[" << par.first << ";" << par.second << "]";
  return out;
}
template <class T>
ostream& operator<<(ostream& out, const set<T>& cont) {
  out << "{";
  for (const auto& x : cont) out << x << ", ";
  out << "}";
  return out;
}
template <class T, class U>
ostream& operator<<(ostream& out, const map<T, U>& cont) {
  out << "{";
  for (const auto& x : cont) out << x << ", ";
  out << "}";
  return out;
}
template <class T>
ostream& operator<<(ostream& out, const vector<T>& v) {
  for (int i = 0; i < (int)v.size(); i++) {
    if (i) out << " ";
    out << v[i];
  }
  out << endl;
  return out;
}
bool ccw(complex<long double> p, complex<long double> a,
         complex<long double> b) {
  if ((conj(a - p) * (b - p)).imag() <= 0)
    return (0);
  else
    return (1);
}
int main() {
  cin.sync_with_stdio(false);
  cout.sync_with_stdio(false);
  int n = get<int>();
  int m = get<int>();
  vector<pair<pair<int, int>, int> > edges;
  for (int i = 0; i < (int)m; i++) {
    int a = get<int>() - 1;
    int b = get<int>() - 1;
    edges.push_back({{a, b}, get<int>()});
  }
  reverse(edges.begin(), edges.end());
  vector<map<int, int> > M(n);
  for (int i = 0; i < (int)n; i++) M[i][infinity] = 0;
  long long int res = 0;
  for (auto e : edges) {
    long long int maxi = M[e.first.second].upper_bound(e.second)->second;
    if (0)
      cout << "Pridavame edge " << e << " v " << M[e.first.second]
           << " co bude teda maxi : " << maxi << endl;
    res = max(maxi + 1, res);
    auto it = M[e.first.first].upper_bound(e.second);
    if (it != M[e.first.first].begin()) {
      it--;
      while (it != M[e.first.first].end() && it->second < maxi + 1) {
        auto itt = it;
        if (0) cout << *it << " je nepotreble, lebo je male " << endl;
        if (it == M[e.first.first].begin()) {
          M[e.first.first].erase(it);
          break;
        } else {
          itt--;
          M[e.first.first].erase(it);
        }
        it = itt;
      }
    }
    if (M[e.first.first].lower_bound(e.second) != M[e.first.first].end() &&
        M[e.first.first].lower_bound(e.second)->second >= maxi + 1)
      continue;
    M[e.first.first][e.second] = maxi + 1;
  }
  cout << res << endl;
}
