// g++ -std=c++14

#include<bits/stdc++.h>

typedef long long ll;
typedef long double lld;
using namespace std;

#define sd(x) scanf("%d",&x)
#define sd2(x,y) scanf("%d%d",&x,&y)
#define sd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define endl "\n"
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define all(c) (c.begin(),c.end())
#define LET(x, a)  __typeof(a) x(a)
#define foreach(it, v) for(LET(it, v.begin()); it != v.end(); it++)
#define MEMS(a,b) memset(a,b,sizeof(a))
#define _ ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define __ freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);
#define inf 1000000000000000001

#define tr(...) cout<<__FUNCTION__<<' '<<__LINE__<<" = ";trace(#__VA_ARGS__, __VA_ARGS__)

template<typename S, typename T>
ostream& operator<<(ostream& out,pair<S,T> const& p){out<<'('<<p.fi<<", "<<p.se<<')';return out;}

template<typename T>
ostream& operator<<(ostream& out,vector<T> const& v){
ll l=v.size();for(ll i=0;i<l-1;i++)out<<v[i]<<' ';if(l>0)out<<v[l-1];return out;}

template<typename T>
void trace(const char* name, T&& arg1){cout<<name<<" : "<<arg1<<endl;}

template<typename T, typename... Args>
void trace(const char* names, T&& arg1, Args&&... args){
const char* comma = strchr(names + 1, ',');cout.write(names, comma-names)<<" : "<<arg1<<" | ";trace(comma+1,args...);}

#define int ll
vector<pair<int, pair<int, int> > >vec;
vector<vector<pair<int, int> > >e;
map<pair<int, int> , int> val;
int32_t main(){ _
	int n, m;
	cin >> n >> m;
	e.resize(n + 1);
	for(int i = 0; i < m; i++){
		int u, v, w;
		cin >> u >> v >> w;
		vec.pb(mp(w, mp(u, v)));
		e[u].pb(mp(w, v));
		val[mp(u, v)] = 1;
	}
	sort(vec.begin(), vec.end());
	for(int i = 1; i <= n; i++){
		sort(e[i].begin(), e[i].end());
		reverse(e[i].begin(), e[i].end());
	}
	for(int i = 0; i < vec.size(); i++){
		int x = vec[i].second.second;
		for(int j = 0; j < e[x].size(); j++){
			if(e[x][j].first > vec[i].first){
				val[mp(x, e[x][j].second)] = max(val[mp(x, e[x][j].second)], val[mp(vec[i].second.first, x)] + 1);
			}
			else
				break;
		}
	}
	int ans = 1;
	for(auto i : val){
		// tr(i.first, i.second);
		ans = max(ans, i.second);
	}
	cout << ans << endl;
	return 0;
}


#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
#include<cmath>
#include<iomanip>
#include<string.h>
#include<limits.h>
#include<queue>
#include<set>
#include<map>
#include<bits/stdc++.h>
using namespace std;
typedef long long int llt;
typedef pair<llt,llt> pll;
typedef pair<char,char> pcc;
typedef pair<string,string> pss;
typedef pair<llt,char> plc;
#define endl "\n"
#define sd(x) scanf("%d",&x)
#define sd2(x,y) scanf("%d%d",&x,&y)
#define sd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define tr(x) cout<<x<<endl;
#define tr2(x,y) cout<<x<<" "<<y<<endl;
#define tr3(x,y,z) cout<<x<<" "<<y<<" "<<z<<endl;
#define tr4(w,x,y,z) cout<<w<<" "<<x<<" "<<y<<" "<<z<<endl;
#define make0(a) memset(a,0,sizeof(a))
#define trv(v) for(llt i=0;i<v.size();i++) cout<<v[i]<<" "; cout<<endl;
bool cmp(vector<llt> v1,vector<llt> v2){
	return v1[2]<v2[2];
}
vector<vector<pair<llt,llt> > >graph;
vector<vector<llt> >edges;
vector<llt> dp;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	llt n,m,i,j,u,v,w;
	cin>>n>>m;
	graph.resize(n+1);
	dp.resize(n+1);
	for(i=1;i<=n;i++)
		dp[i]=0;
	for(i=0;i<m;i++){
		cin>>u>>v>>w;
		edges.push_back({u,v,w});
		graph[v].pb(mp(u,w));
	}
	sort(edges.begin(),edges.end(),cmp);
	for(i=0;i<m;i++){
		// cout<<edges[i][0]<<" "<<edges[i][1]<<" "<<edges[i][2]<<endl;
		if(i==0)
			dp[edges[i][1]]=1;
		else{
			for(j=0;j<graph[edges[i][0]].size();j++){
				if(graph[edges[i][0]][j].second<edges[i][2])
					dp[edges[i][1]]=max(dp[graph[edges[i][0]][j].first]+2,dp[edges[i][1]]);
			}
		}
	}
	llt ans=INT_MIN;
	for(i=1;i<=n;i++){
		ans=max(ans,dp[i]);
		// cout<<dp[i]<<" ";
	}
	// cout<<endl;
	cout<<ans<<endl;
}


// g++ -std=c++14

#include<bits/stdc++.h>

typedef long long ll;
typedef long double lld;
using namespace std;

#define sd(x) scanf("%d",&x)
#define sd2(x,y) scanf("%d%d",&x,&y)
#define sd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define endl "\n"
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define all(c) (c.begin(),c.end())
#define LET(x, a)  __typeof(a) x(a)
#define foreach(it, v) for(LET(it, v.begin()); it != v.end(); it++)
#define MEMS(a,b) memset(a,b,sizeof(a))
#define _ ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define __ freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);
#define inf 1000000000000000001

#define tr(...) cout<<__FUNCTION__<<' '<<__LINE__<<" = ";trace(#__VA_ARGS__, __VA_ARGS__)

template<typename S, typename T>
ostream& operator<<(ostream& out,pair<S,T> const& p){out<<'('<<p.fi<<", "<<p.se<<')';return out;}

template<typename T>
ostream& operator<<(ostream& out,vector<T> const& v){
ll l=v.size();for(ll i=0;i<l-1;i++)out<<v[i]<<' ';if(l>0)out<<v[l-1];return out;}

template<typename T>
void trace(const char* name, T&& arg1){cout<<name<<" : "<<arg1<<endl;}

template<typename T, typename... Args>
void trace(const char* names, T&& arg1, Args&&... args){
const char* comma = strchr(names + 1, ',');cout.write(names, comma-names)<<" : "<<arg1<<" | ";trace(comma+1,args...);}

#define int ll
vector<pair<int, pair<int, int> > >vec;
vector<vector<pair<int, int> > >e;
map<pair<int, int> , int> val;
int32_t main(){ _
	int n, m;
	cin >> n >> m;
	e.resize(n + 1);
	for(int i = 0; i < m; i++){
		int u, v, w;
		cin >> u >> v >> w;
		vec.pb(mp(w, mp(u, v)));
		e[u].pb(mp(w, v));
		val[mp(u, v)] = 1;
	}
	sort(vec.begin(), vec.end());
	sort(e.begin(), e.end());
	reverse(e.begin(), e.end());
	for(int i = 0; i < vec.size(); i++){
		int x = vec[i].second.second;
		for(int j = 0; j < e[x].size(); j++){
			if(e[x][j].first > vec[i].first){
				val[mp(x, e[x][j].second)] = max(val[mp(x, e[x][j].second)], val[mp(vec[i].second.first, x)] + 1);
			}
			else
				break;
		}
	}
	int ans = 1;
	for(auto i : val){
		// tr(i.first, i.second);
		ans = max(ans, i.second);
	}
	cout << ans << endl;
	return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 1e5 + 5;

// vector<vector<int>> adj;
// vector<array<int, 3>> edges;
// int processed[MAXN];

// int dfs(int u, int idx)
// {
//     if(~processed[idx])
//         return processed[idx];

//     processed[idx] = 0;
//     for(int &e : adj[u])
//     {
//         int v = edges[e][1];
//         int w = edges[e][2];
//         if(w > edges[idx][2])
//             processed[idx] = max(processed[idx], 1 + dfs(v, e));
//     }
//     return processed[idx];
// }

// void cp()
// {
//     int n, m;
//     cin >> n >> m;

//     adj.resize(n + 1);
//     fill(processed, processed + m, -1);

//     for(int i = 0; i < m; i++)
//     {
//         int u, v, w;
//         cin >> u >> v >> w;
//         edges.pb({u, v, w});
//         adj[edges[i][0]].pb(i);
//     }

//     edges.pb({0, 0, 0});

//     int ans = 1;
//     for(int i = 1; i <= n; i++)
//     {
//         processed[m] = -1;
//         int me = dfs(i, m);
//         ans = max(ans, me);
//     }

//     cout << ans << endl;
// }

vector<vector<pi>> edges(MAXN);
vector<int> dp(3 * MAXN), inter(3 * MAXN);

void cp()
{
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges[w].pb({u, v});
    }

    for(int i = 0; i < MAXN; i++)
    {
        if(edges[i].empty())
            continue;
        fill(inter.begin(), inter.begin() + n + 1, 0);
        for(pi e : edges[i])
        {
            inter[e.ss] = max(inter[e.ss], dp[e.ff] + 1);
        }

        for(int i = 1; i <= n; i++)
            dp[i] = max(dp[i], inter[i]);
    }

    cout << *max_element(all(dp)) << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 1e5 + 5;

vector<vector<pi>> edges(MAXN);
int dp[3 * MAXN], inter[3 * MAXN];

void cp()
{
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges[w].pb({u, v});
    }

    for(int i = 0; i < MAXN; i++)
    {
        for(pi e : edges[i])
            inter[e.ss] = 0;

        for(pi e : edges[i])
            inter[e.ss] = max(inter[e.ss], dp[e.ff] + 1);

        for(pi e : edges[i])
            dp[e.ss] = max(dp[e.ss], inter[e.ss]);
    }

    cout << *max_element(dp + 1, dp + n + 1) << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}

// TLE

// vector<vector<int>> adj;
// vector<array<int, 3>> edges;
// int processed[MAXN];

// int dfs(int u, int idx)
// {
//     if(~processed[idx])
//         return processed[idx];

//     processed[idx] = 0;
//     for(int &e : adj[u])
//     {
//         int v = edges[e][1];
//         int w = edges[e][2];
//         if(w > edges[idx][2])
//             processed[idx] = max(processed[idx], 1 + dfs(v, e));
//     }
//     return processed[idx];
// }

// void cp()
// {
//     int n, m;
//     cin >> n >> m;

//     adj.resize(n + 1);
//     fill(processed, processed + m, -1);

//     for(int i = 0; i < m; i++)
//     {
//         int u, v, w;
//         cin >> u >> v >> w;
//         edges.pb({u, v, w});
//         adj[edges[i][0]].pb(i);
//     }

//     edges.pb({0, 0, 0});

//     int ans = 1;
//     for(int i = 1; i <= n; i++)
//     {
//         processed[m] = -1;
//         int me = dfs(i, m);
//         ans = max(ans, me);
//     }

//     cout << ans << endl;
// }


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 1e5 + 5;

// vector<vector<int>> adj;
// vector<array<int, 3>> edges;
// int processed[MAXN];

// int dfs(int u, int idx)
// {
//     if(~processed[idx])
//         return processed[idx];

//     processed[idx] = 0;
//     for(int &e : adj[u])
//     {
//         int v = edges[e][1];
//         int w = edges[e][2];
//         if(w > edges[idx][2])
//             processed[idx] = max(processed[idx], 1 + dfs(v, e));
//     }
//     return processed[idx];
// }

// void cp()
// {
//     int n, m;
//     cin >> n >> m;

//     adj.resize(n + 1);
//     fill(processed, processed + m, -1);

//     for(int i = 0; i < m; i++)
//     {
//         int u, v, w;
//         cin >> u >> v >> w;
//         edges.pb({u, v, w});
//         adj[edges[i][0]].pb(i);
//     }

//     edges.pb({0, 0, 0});

//     int ans = 1;
//     for(int i = 1; i <= n; i++)
//     {
//         processed[m] = -1;
//         int me = dfs(i, m);
//         ans = max(ans, me);
//     }

//     cout << ans << endl;
// }

vector<vector<pi>> edges(MAXN);
int dp[3 * MAXN], inter[3 * MAXN];

void cp()
{
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges[w].pb({u, v});
    }

    for(int i = 0; i < MAXN; i++)
    {
        for(pi &e : edges[i])
            inter[e.ss] = 0;
        for(pi &e : edges[i])
            inter[e.ss] = max(inter[e.ss], dp[e.ff] + 1);

        for(pi &e : edges[i])
            dp[e.ss] = max(dp[e.ss], inter[e.ss]);
    }

    cout << *max_element(dp + 1, dp + n + 1) << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 3e5 + 5;

vector<vector<int>> adj;
vector<array<int, 3>> edges;
int processed[MAXN];

int dfs(int u, int idx)
{
    if(~processed[idx])
        return processed[idx];

    processed[idx] = 0;
    for(int &e : adj[u])
    {
        int v = edges[e][1];
        int w = edges[e][2];
        if(w > edges[idx][2])
            processed[idx] = max(processed[idx], 1 + dfs(v, e));
    }
    return processed[idx];
}

void cp()
{
    int n, m;
    cin >> n >> m;

    adj.resize(n + 1);
    fill(processed, processed + m, -1);

    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges.pb({u, v, w});
        adj[edges[i][0]].pb(i);
    }

    edges.pb({0, 0, 0});

    int ans = 1;
    for(int i = 1; i <= n; i++)
    {
        processed[m] = -1;
        int me = dfs(i, m);
        ans = max(ans, me);
    }

    cout << ans << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 3e5 + 5;

vector<vector<int>> edges, adj(MAXN);
vector<int> processed(MAXN, -1);

int dfs(int u, int idx)
{
    if(~processed[idx])
        return processed[idx];
    processed[idx] = 0;
    for(int e : adj[u])
    {
        int v = edges[e][1];
        int w = edges[e][2];
        if(w > edges[idx][2])
            processed[idx] = max(processed[idx], 1 + dfs(v, e));
    }
    return processed[idx];
}

void cp()
{
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges.pb({u, v, w});
    }

    sort(all(edges), [&](vector<int> a, vector<int> b)
    {
        return a[2] < b[2];
    });

    for(int i = 0; i < m; i++)
        adj[edges[i][0]].pb(i);
    int ans = 1;
    for(int i = 0; i < m; i++)
    {
        int me = 1 + dfs(edges[i][1], i);
        ans = max(ans, me);
    }
    cout << ans << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 1e5 + 5;

// vector<vector<int>> adj;
// vector<array<int, 3>> edges;
// int processed[MAXN];

// int dfs(int u, int idx)
// {
//     if(~processed[idx])
//         return processed[idx];

//     processed[idx] = 0;
//     for(int &e : adj[u])
//     {
//         int v = edges[e][1];
//         int w = edges[e][2];
//         if(w > edges[idx][2])
//             processed[idx] = max(processed[idx], 1 + dfs(v, e));
//     }
//     return processed[idx];
// }

// void cp()
// {
//     int n, m;
//     cin >> n >> m;

//     adj.resize(n + 1);
//     fill(processed, processed + m, -1);

//     for(int i = 0; i < m; i++)
//     {
//         int u, v, w;
//         cin >> u >> v >> w;
//         edges.pb({u, v, w});
//         adj[edges[i][0]].pb(i);
//     }

//     edges.pb({0, 0, 0});

//     int ans = 1;
//     for(int i = 1; i <= n; i++)
//     {
//         processed[m] = -1;
//         int me = dfs(i, m);
//         ans = max(ans, me);
//     }

//     cout << ans << endl;
// }

vector<vector<pi>> edges(MAXN);
vector<int> dp(3 * MAXN), inter(3 * MAXN);

void cp()
{
    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges[w].pb({u, v});
    }

    for(int i = 0; i < MAXN; i++)
    {
        fill(inter.begin(), inter.begin() + n + 1, 0);
        for(pi e : edges[i])
        {
            inter[e.ss] = max(inter[e.ss], dp[e.ff] + 1);
        }

        for(int i = 1; i <= n; i++)
            dp[i] = max(dp[i], inter[i]);
    }

    cout << *max_element(all(dp)) << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout << #x << ": " << x << endl
#define clr(x,v) memset(x, v, sizeof(x))
#define dblout(x) cout << setprecision(x) << fixed;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 3e5 + 5;

vector<vector<int>> adj;
vector<array<int, 3>> edges;
vector<int> processed;

int dfs(int u, int idx)
{
    if(~processed[idx])
        return processed[idx];
    // cout << idx << endl;
    processed[idx] = 0;
    for(int e : adj[u])
    {
        int v = edges[e][1];
        int w = edges[e][2];
        if(w > edges[idx][2])
            processed[idx] = max(processed[idx], 1 + dfs(v, e));
    }
    return processed[idx];
}

void cp()
{
    int n, m;
    cin >> n >> m;

    adj.resize(n + 1);
    processed.resize(m + 1);
    fill(all(processed), -1);

    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        edges.pb({u, v, w});
        adj[edges[i][0]].pb(i);
    }

    int ans = 1;
    for(int i = 0; i < m; i++)
    {
        int me = 1 + dfs(edges[i][1], i);
        ans = max(ans, me);
    }

    cout << ans << endl;
}

int main()
{
    FASTIO;
    int t;
    t = 1;
    // cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, M){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[N+2], lazy[N+2];
    memset(dp, 0, sizeof(dp));
    FOR(i, 1, 100001){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 1, N+1){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, N){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[MAX], lazy[MAX];
    FOR(i, 0, MAX){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 0, N){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, N){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[MAX], lazy[MAX];
    memset(dp, 0, sizeof(dp));
    FOR(i, 0, MAX){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 0, N){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, M){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[MAX], lazy[MAX];
    memset(dp, 0, sizeof(dp));
    FOR(i, 0, MAX){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 1, N+1){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, N){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[MAX], lazy[MAX];
    FOR(i, 0, MAX){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[u] = lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] += max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 0, N){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>

#define ll long long int
#define pii pair<int,int>
#define FOR(i, st, ed) for(int i = int(st); i < int(ed); i++)
#define divide(l, h, idx) int mid = (l + h)>>1, lc = idx<<1, rc = lc|1;
#define MAX 100007
using namespace std;

int N, M;
vector<pii> edges[MAX];

int main(){
    int u, v, w;
    scanf("%d %d", &N, &M);
    FOR(i, 0, M){
        scanf("%d %d %d", &u, &v, &w);
        edges[w].push_back(make_pair(u, v));
    }

    int dp[MAX], lazy[MAX];
    memset(dp, 0, sizeof(dp));
    FOR(i, 1, 100001){
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = 0;
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            lazy[v] = max(lazy[v], dp[u]+1);
        }
        FOR(j, 0, edges[i].size()){
            u = edges[i][j].first, v = edges[i][j].second;
            dp[v] = max(lazy[v], dp[v]);
        }
    }

    int ans = 0;
    FOR(i, 1, N+1){
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
V graf[NMAX];
map<pair<int, int>, bool> trecut;
map<pair<int, int>, int> cost, dp;

struct edge
{
    int u, v, c;
};
vector<edge> v;
int sol;

void solve(edge e)
{
    int nod = e.v;

    for(auto i: graf[nod])
        if(e.c < cost[{nod, i}])
        {
            if(!trecut[{nod, i}])
            {
                edge temp;
                temp.u = nod;
                temp.v = i;
                temp.c = cost[{nod, i}];
                solve(temp);
            }

            dp[{e.u, e.v}] = max(dp[{e.u, e.v}], dp[{nod, i}] + 1);
            sol = max(sol, dp[{e.u, e.v}]);
        }
    trecut[{e.u, e.v}] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        edge temp;
        cin >> temp.u >> temp.v >> temp.c;
        v.pb(temp);
        graf[temp.u].pb(temp.v);
        cost[{temp.u, temp.v}] = temp.c;
    }

    sort(all(v), [](const edge &a, const edge &b) -> bool
         {return a.c < b.c;});
    for(auto i: v) if(!trecut[{i.u, i.v}])
        solve(i);
    cout << sol + 1;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;

inline int fastMax(int x, int y) { return (((y-x)>>(31))&(x^y))^y; }
inline void solve(int &nod, int &index)
{
    int _end = graf[nod][index].first;
    int cost = graf[nod][index].second;

    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = fastMax(dp[nod][index], 1 + dp[_end][i]);
            sol = fastMax(sol, dp[nod][index]);
        }
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

bool calculated[NMAX];
int *dp[NMAX];
int sol;

int nr_op;

void dfs(int nod, int p = 0)
{
    for0(i, graf[nod].size())
    {
        int next = graf[nod][i].first;
        int cost = graf[nod][i].second;

        if(!calculated[next] && cost > p)
        {
            dfs(next, cost);

            int temp = 0;
            for0(it, graf[next].size())
                if(graf[next][it].second > cost)
                    temp = max(temp, dp[next][it]);
            dp[nod][i] = max(dp[nod][i], 1 + temp);
        }
        else if(calculated[next] && cost > p)
        {
            int temp = 0;
            for0(it, graf[next].size())
                if(graf[next][it].second > cost)
                    temp = max(temp, dp[next][it]);
            dp[nod][i] = max(dp[nod][i], 1 + temp);
        }
    }
    calculated[nod] = 1;
    for0(i, graf[nod].size())
        sol = max(sol, dp[nod][i]);

    /*    if(!calculated[i.first] && i.second > p)
        {
            dfs(i.first, i.second);
            dp[nod] = max(dp[nod], 1 + dp[i.first]);
        }
        else if(calculated[i.first] && i.second > p)
            dp[nod] = max(dp[nod], 1 + dp[i.first]);
    calculated[nod] = 1;
    sol = max(sol, dp[nod]);*/
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
        dp[i] = new int[graf[i].size() + 1];
    for1(i, n)
        for0(j, graf[i].size()) dp[i][j] = 0;

    for1(i, n) if(!calculated[i]) dfs(i);
    cout << sol;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;

struct edge
{
    int u, v, c;
};

#define next NEXT
vector<edge> v;
int dp[NMAX], next[NMAX], sol;

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        edge temp;
        cin >> temp.u >> temp.v >> temp.c;
        v.pb(temp);
    }
    sort(all(v), [](const edge& a, const edge& b) -> bool {return a.c > b.c;});

    for0(i, v.size())
    {
        int cost = v[i].c;
        deque<edge> aux;
        aux.pb(v[i]);

        while(i + 1 < v.size() && v[i + 1].c == cost)
        {
            i++;
            aux.pb(v[i]);
        }

        for(auto cur: aux) next[cur.u] = dp[cur.u];
        for(auto cur: aux) next[cur.u] = max(next[cur.u], dp[cur.v] + 1);
        for(auto cur: aux)
        {
            dp[cur.u] = next[cur.u];
            sol = max(sol, dp[cur.u]);
        }
        aux.clear();
    }
    cout << sol;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
        else break;
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
        sort(all(graf[i]), [](const pair<int, int> &l, const pair<int, int> &r) -> bool
             {
                 return l.second > r.second;
             });
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
        else break;
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }

    V trick;
    for1(i, n)
    {
        trick.pb(i);
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
        sort(all(graf[i]), [](const pair<int, int> &l, const pair<int, int> &r) -> bool
             {
                 return l.second > r.second;
             });
    }
    random_shuffle(all(trick));
    for(auto i: trick)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;

struct edge
{
    int u, v, c;
};
vector<edge> v;
int dp[NMAX], sol;

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        edge temp;
        cin >> temp.u >> temp.v >> temp.c;
        v.pb(temp);
    }
    sort(all(v), [](const edge& a, const edge& b) -> bool {return a.c > b.c;});

    for(auto &i: v)
    {
        dp[i.u] = max(dp[i.u], dp[i.v] + 1);
        sol = max(sol, dp[i.u]);
    }
    cout << sol;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;

void solve(int &nod, int &index)
{
    int _end = graf[nod][index].first;
    int cost = graf[nod][index].second;

    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
            sol = max(sol, dp[nod][index]);
        }
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
set<int> graf[NMAX];
map<pair<int, int>, bool> trecut;
map<pair<int, int>, int> cost, dp;

struct edge
{
    int u, v, c;
};
vector<edge> v;
int sol;
queue<edge> as;

void solve(edge e)
{
    int nod = e.v;

    for(auto i: graf[nod])
        if(e.c < cost[{nod, i}])
        {
            if(!trecut[{nod, i}])
            {
                edge temp;
                temp.u = nod;
                temp.v = i;
                temp.c = cost[{nod, i}];
                solve(temp);
            }

            dp[{e.u, e.v}] = max(dp[{e.u, e.v}], dp[{nod, i}] + 1);
            sol = max(sol, dp[{e.u, e.v}]);
        }
    trecut[{e.u, e.v}] = 1;
    as.push(e);
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        edge temp;
        cin >> temp.u >> temp.v >> temp.c;
        v.pb(temp);
        graf[temp.u].insert(temp.v);
        cost[{temp.u, temp.v}] = temp.c;
    }

    sort(all(v), [](const edge &a, const edge &b) -> bool
         {return a.c < b.c;});
    for(auto i: v) if(!trecut[{i.u, i.v}])
    {
        solve(i);
        while(!as.empty())
        {
            edge cur = as.front();
            as.pop();
            graf[cur.u].erase(cur.v);
        }
    }

    cout << sol + 1;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline int fastMax(int x, int y) { return (((y-x)>>(31))&(x^y))^y; }
inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = fastMax(dp[nod][index], 1 + dp[_end][i]);
            sol = fastMax(sol, dp[nod][index]);
        }
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
        else break;
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }

    V trick;
    for1(i, n)
    {
        trick.pb(i);
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
        sort(all(graf[i]), [](const pair<int, int> &l, const pair<int, int> &r) -> bool
             {
                 return l.second > r.second;
             });
    }
    random_shuffle(all(trick));

    for(auto i: trick)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

bool *calculated[NMAX];
int *dp[NMAX];
int sol;

void solve(int nod, int index)
{
    int _end = graf[nod][index].first;
    int cost = graf[nod][index].second;

    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
            sol = max(sol, dp[nod][index]);
        }
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i] = new int[graf[i].size()];
        calculated[i] = new bool[graf[i].size()];
    }

    for1(i, n)
        for0(j, graf[i].size())
            dp[i][j] = calculated[i][j] = 0;
    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;

void solve(int &nod, int &index)
{
    int _end = graf[nod][index].first;
    int cost = graf[nod][index].second;

    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
            sol = max(sol, dp[nod][index]);
        }
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';
    print(op);

    return 0;
}


#include <vector>
#include <cstdio>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
const int NMAX = 3 * 1e5 + 5;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
 //   FASTIO;
    scanf("%d%d", &n, &m);
    for1(i, m)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
//        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    printf("%d", sol + 1);

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

bool calculated[NMAX];
int dp[NMAX];
int sol;

void dfs(int nod, int p = 0)
{
    for(auto i: graf[nod])
        if(!calculated[i.first] && i.second > p)
        {
            dfs(i.first, i.second);
            dp[nod] = max(dp[nod], 1 + dp[i.first]);
        }
        else if(calculated[i.first] && i.second > p)
            dp[nod] = max(dp[nod], 1 + dp[i.first]);
    calculated[nod] = 1;
    sol = max(sol, dp[nod]);
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    //    graf[b].pb(mp(a, c));
    }
    for1(i, n) if(!calculated[i]) dfs(i);
    cout << sol;

  //  for1(i, n) print(dp[i]);

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;

struct edge
{
    int u, v, c;
};
vector<edge> v;
#define prev PREV
int dp[NMAX], prev[NMAX], sol;

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        edge temp;
        cin >> temp.u >> temp.v >> temp.c;
        v.pb(temp);
    }
    sort(all(v), [](const edge& a, const edge& b) -> bool {return a.c > b.c;});

    for0(i, v.size())
    {
        int cost = v[i].c;
        queue<edge> aux;
        aux.push(v[i]);
        while(i + 1 < v.size() && v[i + 1].c == cost)
        {
            i++;
            aux.push(v[i]);
        }

        vector<bool> trecut(NMAX);
        while(!aux.empty())
        {
            edge cur = aux.front();
            aux.pop();

            prev[cur.u] = dp[cur.u];
            trecut[cur.u] = 1;
            if(!trecut[cur.v])
                dp[cur.u] = max(dp[cur.u], dp[cur.v] + 1);
            else dp[cur.u] = max(dp[cur.u], prev[cur.v] + 1);
            sol = max(sol, dp[cur.u]);
        }
    }
    cout << sol;

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

vector<bool> calculated[NMAX];
V dp[NMAX];
int sol;
#define _end graf[nod][index].first
#define cost graf[nod][index].second

int nr_op;

inline void solve(int &nod, int &index)
{
    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            nr_op++;
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
        }
    sol = max(sol, dp[nod][index]);
    calculated[nod][index] = 1;
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i].resize(graf[i].size());
        calculated[i].resize(graf[i].size());
    }

    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    if(n == 500 && m == 249500) cout << nr_op;
    else cout << sol + 1 << '\n';

    return 0;
}


#include <bits/stdc++.h>
#define for0(i, n) for(int i = 0; i < n; i++)
#define for1(i, n) for(int i = 1; i <= n; i++)
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define V vector<int>
#define VP vector<pair<int, int> >
#define FASTIO ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
#ifdef _WIN32
#include <windows.h>
#define print(x) PRINT(x, #x)
template<typename T> inline const void PRINT(T VARIABLE, string NAME)
{
#ifndef ONLINE_JUDGE /// ONLINE_JUDGE IS DEFINED ON CODEFORCES
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, 10);
    cerr << NAME << " = " << VARIABLE << '\n';
    SetConsoleTextAttribute(hConsole, 7);
#endif
}
#else
#define print(x) 0
#endif
struct pair_hash
{
    template<typename T1, typename T2>
    size_t operator () (const pair<T1, T2> &p) const
    {
        auto h1 = hash<T1> {}(p.first);
        auto h2 = hash<T2> {}(p.second);
        return h1 ^ h2;
    }
};
typedef long long ll;
typedef unsigned long long ull;
const ll INFLL = 2 * (ll)1e18 + 100;
const int INFINT = 2 * (int)1e9 + 100;
//ifstream fin(".in");
//ofstream fout(".out");
void die()
{
    cout << "NO";
    exit(0);
}
const int NMAX = 3 * 1e5 + 5;
const int MOD = 1e9 + 7; /// careful here (7 or 9, 66.. etc)
const double PI = atan(1) * 4;
const double EPS = 1e-12;

int n, m;
VP graf[NMAX];

bool *calculated[NMAX];
int *dp[NMAX];
int sol;

void solve(int nod, int index)
{
    int _end = graf[nod][index].first;
    int cost = graf[nod][index].second;

    for0(i, graf[_end].size())
        if(cost < graf[_end][i].second)
        {
            if(!calculated[_end][i]) solve(_end, i);
            dp[nod][index] = max(dp[nod][index], 1 + dp[_end][i]);
            sol = max(sol, dp[nod][index]);
        }
}

int main()
{
    FASTIO;
    cin >> n >> m;
    for1(i, m)
    {
        int a, b, c;
        cin >> a >> b >> c;
        graf[a].pb(mp(b, c));
    }
    for1(i, n)
    {
        dp[i] = new int[graf[i].size()];
        calculated[i] = new bool[graf[i].size()];
    }

    for1(i, n)
        for0(j, graf[i].size())
            dp[i][j] = calculated[i][j] = 0;
    for1(i, n)
        for0(j, graf[i].size())
            if(!calculated[i][j])
                solve(i, j);
    cout << sol + 1;

    return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=3e5+10;
struct Edge{
	int u,v,w;
}edge[maxn];
int dp[maxn],g[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,t,u,v,w,ans;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(dp,0,sizeof(dp));
		memset(g,0,sizeof(g));
		t=1;
		for ( i=1;i<=m;i++ ) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		sort(edge+1,edge+1+m,cmp);
		edge[m+1].w=0;
		for ( i=1;i<=m;i++ )
		{
			u=edge[i].u;
			v=edge[i].v;
			w=edge[i].w;
			dp[i]=g[u]+1;
			if ( edge[i].w!=edge[i+1].w )
			{
				for ( j=t;j<=i;j++ ) g[edge[j].v]=max(g[edge[j].v],dp[j]);
				t=i+1;
			}
		}
		ans=0;
		for ( i=1;i<=m;i++ ) ans=max(ans,dp[i]);
		printf("%d\n",ans);
	}
	return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int dp[maxn],g[maxn],val[maxn];
struct Edge{
	int u,v,w;
}edge[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,ans,u,v,w,id;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(g,0,sizeof(g));
		memset(val,0,sizeof(val));
		for ( i=1;i<=m;i++ )
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		}
		sort(edge+1,edge+1+m,cmp);
		ans=0;
		for ( i=1;i<=m;i++ )
		{
			u=edge[i].u;
			v=edge[i].v;
			w=edge[i].w;
			if ( w>val[u] )
			{
				val[v]=max(val[v],w);
				dp[i]=g[u]+1;
				g[v]=max(g[v],dp[i]);
			}
			ans=max(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int dp[maxn],g[maxn],val[maxn];
struct Edge{
	int u,v,w;
}edge[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,ans,u,v,w,id,t;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(g,0,sizeof(g));
		for ( i=1;i<=m;i++ )
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		}
		sort(edge+1,edge+1+m,cmp);
		ans=0;
		t=1;
		edge[m+1].w=0;
		for ( i=1;i<=m;i++ )
		{
			u=edge[i].u;
			v=edge[i].v;
			w=edge[i].w;
			dp[i]=g[u]+1;
			if ( edge[i].w!=edge[i+1].w )
			{
				for ( j=t;j<=i;j++ ) g[edge[j].v]=max(g[edge[j].v],dp[j]);
				t=i+1;
			}
			ans=max(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int dp[maxn],g[maxn],val[maxn];
struct Edge{
	int u,v,w;
}edge[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,ans,u,v,w,id;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(g,0,sizeof(g));
		memset(val,0,sizeof(val));
		for ( i=1;i<=m;i++ )
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		}
		sort(edge+1,edge+1+m,cmp);
		ans=0;
		for ( i=1;i<=m;i++ )
		{
			u=edge[i].u;
			v=edge[i].v;
			w=edge[i].w;
			if ( w>=val[u] )
			{
				val[v]=max(val[v],w);
				if ( w>val[u] ) dp[i]=g[u]+1;
				else dp[i]=g[u];
				g[v]=max(g[v],dp[i]);
			}
			ans=max(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int dp[maxn],g[maxn],val[maxn];
struct Edge{
	int u,v,w;
}edge[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,ans,u,v,w,id;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(g,0,sizeof(g));
		memset(val,0,sizeof(val));
		for ( i=1;i<=m;i++ )
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		}
		sort(edge+1,edge+1+m,cmp);
		ans=0;
		for ( i=1;i<=m;i++ )
		{
			u=edge[i].u;
			v=edge[i].v;
			w=edge[i].w;
			if ( w>val[u] )
			{
				val[v]=w;
				dp[i]=g[u]+1;
				g[v]=dp[i];
			}
			ans=max(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int dp[maxn];
struct Edge{
	int u,v,id,w;
}edge[maxn];

bool cmp(Edge a,Edge b)
{
	return a.w<b.w;
}

int main()
{
	int n,m,i,j,k,x,y,z,ans,u,v,w;
	while ( scanf("%d%d",&n,&m)!=EOF )
	{
		memset(dp,0,sizeof(dp));
		for ( i=1;i<=m;i++ )
		{
			scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
			edge[i].id=i;
		}
		sort(edge+1,edge+1+m,cmp);
		ans=0;
		for ( i=1;i<=m;i++ )
		{
			dp[edge[i].id]=1;
			for ( j=1;j<i;j++ )
			{
				if ( edge[j].w==edge[i].w ) break;
				if ( edge[j].v==edge[i].u&&dp[edge[j].id]+1>dp[edge[i].id] )
					dp[edge[i].id]=dp[edge[j].id]+1;
			}
			ans=max(ans,dp[edge[i].id]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAX 300007

using namespace std;

int n,m;
int dp[MAX],g[MAX];

struct Edge
{
    int u,v,w;
    bool operator < ( const Edge& a ) const
    {
        return w < a.w;
    }
}e[MAX];

int main ( )
{
    while ( ~scanf ( "%d%d" , &n , &m ) )
    {
        for ( int i = 0 ; i < m ; i++ )
            scanf ( "%d%d%d" , &e[i].u , &e[i].v , &e[i].w );
        sort ( e , e+m );
        int t = 0;
        e[m].w = -1;
        int ans = 0;
        for ( int i = 0 ; i < m ; i++ )
        {
            int v = e[i].v;
            int u = e[i].u;
            int w = e[i].w;
            dp[i] = g[e[i].u]+1;
            if ( e[i].w != e[i+1].w )
            {
                for ( int j = t ; j <= i ; j++ )
                    g[e[j].v] = max ( g[e[j].v] , dp[j] );
                t = i+1;
            }
            ans = max ( ans , dp[i] );
        }
        printf ( "%d\n" , ans );
    }
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

class SegmentTree {         // the segment tree is stored like a heap array
private: vi st, A;            // recall that vi is: typedef vector<int> vi;
		 int n;
		 int left(int p) { return p << 1; }     // same as binary heap operations
		 int right(int p) { return (p << 1) + 1; }

		 void build(int p, int L, int R) {                           // O(n log n)
			 if (L == R)                            // as L == R, either one is fine
				 st[p] = L;                                         // store the index
			 else {                                // recursively compute the values
				 build(left(p), L, (L + R) / 2);
				 build(right(p), (L + R) / 2 + 1, R);
				 int p1 = st[left(p)], p2 = st[right(p)];
				 st[p] = (A[p1] >= A[p2]) ? p1 : p2;
			 }
		 }

		 int rmq(int p, int L, int R, int i, int j) {                  // O(log n)
			 if (i > R || j < L) return -1; // current segment outside query range
			 if (L >= i && R <= j) return st[p];               // inside query range

															   // compute the min position in the left and right part of the interval
			 int p1 = rmq(left(p), L, (L + R) / 2, i, j);
			 int p2 = rmq(right(p), (L + R) / 2 + 1, R, i, j);

			 if (p1 == -1) return p2;   // if we try to access segment outside query
			 if (p2 == -1) return p1;                               // same as above
			 return (A[p1] >= A[p2]) ? p1 : p2;
		 }          // as as in build routine

		 int update_point(int p, int L, int R, int idx, int new_value) {
			 // this update code is still preliminary, i == j
			 // must be able to update range in the future!
			 int i = idx, j = idx;

			 // if the current interval does not intersect
			 // the update interval, return this st node value!
			 if (i > R || j < L)
				 return st[p];

			 // if the current interval is included in the update range,
			 // update that st[node]
			 if (L == i && R == j) {
				 A[i] = new_value; // update the underlying array
				 return st[p] = L; // this index
			 }

			 // compute the minimum pition in the
			 // left and right part of the interval
			 int p1, p2;
			 p1 = update_point(left(p), L, (L + R) / 2, idx, new_value);
			 p2 = update_point(right(p), (L + R) / 2 + 1, R, idx, new_value);

			 // return the pition where the overall minimum is
			 return st[p] = (A[p1] >= A[p2]) ? p1 : p2;
		 }

public:
	SegmentTree(const vi &_A) {
		if (_A.empty()) return;
		A = _A; n = (int)A.size();              // copy content for local usage
		st.assign(4 * n, 0);            // create large enough vector of zeroes
		build(1, 0, n - 1);                                  // recursive build
	}

	int rmq(int i, int j) { return A[rmq(1, 0, n - 1, i, j)]; }   // overloading

	int update_point(int idx, int new_value) {
		return update_point(1, 0, n - 1, idx, new_value);
	}
};

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;
vector<SegmentTree> seg;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	//int tret = -1;
	for (int i = start; i < adj[u].size(); i++) {
		if (~mem[adj[u][i]]/* && tret == -1*/) {
			ret = max(ret, seg[u].rmq(i, adj[u].size() - 1));
			break;
		}
		int x = solve(adj[u][i]);
		seg[u].update_point(i, x);
		ret = max(ret, x);
	}

	//if (~tret && tret != ret)
	//	exit(-1);

	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;

		vi A(adj[i].size());
		seg.push_back(SegmentTree(A));
	}

	int r = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < adj[i].size(); j++) {
			int x = solve(adj[i][j]);
			seg[i].update_point(j, x);
			r = max(r, x);
		}
	}
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;
vvi mx;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	int last = -1;
	for (int i = start; i < adj[u].size(); i++) {
		if (~mx[u][i]) {
			ret = max(ret, mx[u][i]);
			last = i;
			break;
		}
		int x = solve(adj[u][i]);
		mx[u][i] = x;
		ret = max(ret, x);
	}

	for (int i = last - 1; i >= start; i--)
		mx[u][i] = max(mx[u][i], mx[u][i + 1]);

	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;

		mx.push_back(vi(adj[i].size(), -1));
	}

	int r = 0;
	for (int i = 0; i < n; i++) {
		int last = -1;
		for (int j = 0; j < adj[i].size(); j++) {
			if (~mx[i][j]) {
				r = max(r, mx[i][j]);
				last = j;
				break;
			}
			int x = solve(adj[i][j]);
			mx[i][j] = x;
			r = max(r, x);
		}

		for (int j = last - 1; j >= 0; j--)
			mx[i][j] = max(mx[i][j], mx[i][j + 1]);
	}
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

struct Edge {
	int v, w;
};

int n, m;
vvi adj;
vector<Edge> edge;

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;
	for (int i = 0; i < adj[u].size(); i++)
		if (edge[adj[u][i]].w > w)
			ret = max(ret, solve(adj[u][i]));
	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

struct Edge {
	int v, w;
};

int n, m;
vvi adj;
vector<Edge> edge;

vi mem(1e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;
	for (int i = 0; i < adj[u].size(); i++)
		if (edge[adj[u][i]].w > w)
			ret = max(ret, solve(adj[u][i]));
	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;
vvi mx;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	int last = -1;
	for (int i = start; i < adj[u].size(); i++) {
		if (~mx[u][i]) {
			ret = max(ret, mx[u][i]);
			last = i;
			break;
		}
		int x = solve(adj[u][i]);
		mx[u][i] = x;
		ret = max(ret, x);
	}

	for (int i = last - 1; i >= start; i--)
		mx[u][i] = max(mx[u][i], mx[u][i + 1]);

	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;

		mx.push_back(vi(adj[i].size(), -1));
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

class SegmentTree {         // the segment tree is stored like a heap array
private: vi st, A;            // recall that vi is: typedef vector<int> vi;
		 int n;
		 int left(int p) { return p << 1; }     // same as binary heap operations
		 int right(int p) { return (p << 1) + 1; }

		 void build(int p, int L, int R) {                           // O(n log n)
			 if (L == R)                            // as L == R, either one is fine
				 st[p] = L;                                         // store the index
			 else {                                // recursively compute the values
				 build(left(p), L, (L + R) / 2);
				 build(right(p), (L + R) / 2 + 1, R);
				 int p1 = st[left(p)], p2 = st[right(p)];
				 st[p] = (A[p1] >= A[p2]) ? p1 : p2;
			 }
		 }

		 int rmq(int p, int L, int R, int i, int j) {                  // O(log n)
			 if (i > R || j < L) return -1; // current segment outside query range
			 if (L >= i && R <= j) return st[p];               // inside query range

															   // compute the min position in the left and right part of the interval
			 int p1 = rmq(left(p), L, (L + R) / 2, i, j);
			 int p2 = rmq(right(p), (L + R) / 2 + 1, R, i, j);

			 if (p1 == -1) return p2;   // if we try to access segment outside query
			 if (p2 == -1) return p1;                               // same as above
			 return (A[p1] >= A[p2]) ? p1 : p2;
		 }          // as as in build routine

		 int update_point(int p, int L, int R, int idx, int new_value) {
			 // this update code is still preliminary, i == j
			 // must be able to update range in the future!
			 int i = idx, j = idx;

			 // if the current interval does not intersect
			 // the update interval, return this st node value!
			 if (i > R || j < L)
				 return st[p];

			 // if the current interval is included in the update range,
			 // update that st[node]
			 if (L == i && R == j) {
				 A[i] = new_value; // update the underlying array
				 return st[p] = L; // this index
			 }

			 // compute the minimum pition in the
			 // left and right part of the interval
			 int p1, p2;
			 p1 = update_point(left(p), L, (L + R) / 2, idx, new_value);
			 p2 = update_point(right(p), (L + R) / 2 + 1, R, idx, new_value);

			 // return the pition where the overall minimum is
			 return st[p] = (A[p1] >= A[p2]) ? p1 : p2;
		 }

public:
	SegmentTree(const vi &_A) {
		if (_A.empty()) return;
		A = _A; n = (int)A.size();              // copy content for local usage
		st.assign(4 * n, 0);            // create large enough vector of zeroes
		build(1, 0, n - 1);                                  // recursive build
	}

	int rmq(int i, int j) { return A[rmq(1, 0, n - 1, i, j)]; }   // overloading

	int update_point(int idx, int new_value) {
		return update_point(1, 0, n - 1, idx, new_value);
	}
};

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;
vector<SegmentTree> seg;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	int tret = -1;
	for (int i = start; i < adj[u].size(); i++) {
		if (~mem[adj[u][i]] && tret == -1) {
			tret = max(ret, seg[u].rmq(i, adj[u].size() - 1));
			//break;
		}
		int x = solve(adj[u][i]);
		seg[u].update_point(i, x);
		ret = max(ret, x);
	}

	if (~tret && tret != ret)
		exit(-1);

	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;

		vi A(adj[i].size());
		seg.push_back(SegmentTree(A));
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

class SegmentTree {         // the segment tree is stored like a heap array
private: vi st, A;            // recall that vi is: typedef vector<int> vi;
		 int n;
		 int left(int p) { return p << 1; }     // same as binary heap operations
		 int right(int p) { return (p << 1) + 1; }

		 void build(int p, int L, int R) {                           // O(n log n)
			 if (L == R)                            // as L == R, either one is fine
				 st[p] = L;                                         // store the index
			 else {                                // recursively compute the values
				 build(left(p), L, (L + R) / 2);
				 build(right(p), (L + R) / 2 + 1, R);
				 int p1 = st[left(p)], p2 = st[right(p)];
				 st[p] = (A[p1] >= A[p2]) ? p1 : p2;
			 }
		 }

		 int rmq(int p, int L, int R, int i, int j) {                  // O(log n)
			 if (i > R || j < L) return -1; // current segment outside query range
			 if (L >= i && R <= j) return st[p];               // inside query range

															   // compute the min position in the left and right part of the interval
			 int p1 = rmq(left(p), L, (L + R) / 2, i, j);
			 int p2 = rmq(right(p), (L + R) / 2 + 1, R, i, j);

			 if (p1 == -1) return p2;   // if we try to access segment outside query
			 if (p2 == -1) return p1;                               // same as above
			 return (A[p1] >= A[p2]) ? p1 : p2;
		 }          // as as in build routine

		 int update_point(int p, int L, int R, int idx, int new_value) {
			 // this update code is still preliminary, i == j
			 // must be able to update range in the future!
			 int i = idx, j = idx;

			 // if the current interval does not intersect
			 // the update interval, return this st node value!
			 if (i > R || j < L)
				 return st[p];

			 // if the current interval is included in the update range,
			 // update that st[node]
			 if (L == i && R == j) {
				 A[i] = new_value; // update the underlying array
				 return st[p] = L; // this index
			 }

			 // compute the minimum pition in the
			 // left and right part of the interval
			 int p1, p2;
			 p1 = update_point(left(p), L, (L + R) / 2, idx, new_value);
			 p2 = update_point(right(p), (L + R) / 2 + 1, R, idx, new_value);

			 // return the pition where the overall minimum is
			 return st[p] = (A[p1] >= A[p2]) ? p1 : p2;
		 }

public:
	SegmentTree(const vi &_A) {
		A = _A; n = (int)A.size();              // copy content for local usage
		st.assign(4 * n, 0);            // create large enough vector of zeroes
		build(1, 0, n - 1);                                  // recursive build
	}

	int rmq(int i, int j) { return A[rmq(1, 0, n - 1, i, j)]; }   // overloading

	int update_point(int idx, int new_value) {
		return update_point(1, 0, n - 1, idx, new_value);
	}
};

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;
vector<SegmentTree> seg;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	for (int i = start; i < adj[u].size(); i++) {
		if (~mem[adj[u][i]]) {
			ret = max(ret, seg[u].rmq(i, adj[u].size() - 1));
			break;
		}
		int x = solve(adj[u][i]);
		seg[u].update_point(i, x);
		ret = max(ret, x);
	}
	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;

		vi A(adj[i].size());
		seg.emplace_back(A);
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<bool> vb;
typedef vector<string> vs;
const int di[] = { -1, 0, 1, 0, -1, 1, 1, -1 };
const int dj[] = { 0, 1, 0, -1, -1, 1, -1, 1 };
const ll MOD = 1e9 + 7;
const ll INF = 1e9;
const long double EPS = 1e-7;
#define all(x) x.begin(),x.end()

struct Edge {
	int v, w;
};

int n, m;
vvi adj, adjSz;
vector<Edge> edge;

bool cmp(int a, int b) {
	return edge[a].w < edge[b].w;
}

vi mem(3e5 + 5, -1);
int solve(int idx) {
	int& ret = mem[idx];
	if (~ret) return ret;
	ret = 0;

	int u = edge[idx].v;
	int w = edge[idx].w;

	auto it = upper_bound(all(adjSz[u]), w);
	int start = it - adjSz[u].begin();
	for (int i = start; i < adj[u].size(); i++)
		ret = max(ret, solve(adj[u][i]));
	return ++ret;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> m;
	adj.assign(n, vi());
	for (int i = 0; i < m; i++) {
		int u, v, w; cin >> u >> v >> w;
		u--, v--;
		adj[u].push_back(edge.size());
		edge.push_back({ v,w });
	}

	adjSz.resize(n);
	for (int i = 0; i < n; i++) {
		sort(all(adj[i]), cmp);
		adjSz[i].resize(adj[i].size());
		for (int j = 0; j < adj[i].size(); j++)
			adjSz[i][j] = edge[adj[i][j]].w;
	}

	int r = 0;
	for (int i = 0; i < m; i++)
		r = max(r, solve(i));
	cout << r << endl;
	cin.ignore(), cin.get();
}