/**
 * Created by hiramekun at 22:21 on 2019-11-09.
 */
#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using vl = vector<ll>;
using vvl = vector<vl>;
using vb = vector<bool>;
using P = pair<ll, ll>;
template<typename T> using pq = priority_queue<T>;
template<typename T> using minpq = priority_queue<T, vector<T>, greater<T>>;
template<typename T, typename K> using ump = unordered_map<T, K>;
const ll dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const ll mod = 1000000007;
const ll inf = ll(1e9);
const ll e5 = ll(1e5);
const ll ll_inf = ll(1e9) * ll(1e9);

#define rep(i, n) for(ll i = 0; i < (ll)(n); i++)
#define repr(i, n) for(ll i = ll(n - 1); i >= 0; i--)
#define each(i, mp) for(auto& i:mp)
#define eb emplace_back
#define F first
#define S second
#define all(obj) (obj).begin(), (obj).end()

template<class T>
ostream &operator<<(ostream &out, const vector<T> &list) {
    ll n = list.size();
    rep(i, n) out << list[i] << ' ';
    return out;
}

template<class T>
istream &operator>>(istream &in, vector<T> &list) {
    ll n = list.size();
    rep(i, n) in >> list[i];
    return in;
}

template<class T>
ostream &operator<<(ostream &out, const vector<vector<T>> &list) {
    ll n = list.size();
    rep(i, n) out << list[i] << '\n';
    return out;
}

/* ------------- ANSWER ------------- */
/* ---------------------------------- */
struct edge {
    ll from, to, cost;

    edge(ll from, ll to, ll cost) : from(from), to(to), cost(cost) {};
};

typedef vector<edge> ve;
typedef vector<ve> vve;

class Graph {
private:
    ll v;
    vve table;
    vl d;
    vl pre; // 最短路の直前の頂点
    ve edges;
public:
    // v: 頂点数
    explicit Graph(ll v) : v(v) {
        table.resize(v);
        pre.resize(v, -1);
        d.resize(v, ll_inf);
    }

    void add(ll from, ll to, ll cost = 1) {
        edge e1(from, to, cost);
        table[from].emplace_back(e1);
        edges.emplace_back(e1);
    }

    // O(v + e)
    // http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_4_B
    vl sort() {
        stack<ll> st; // 入力次数が0の頂点集合
        vl in_d(v);
        for (const auto ele: edges) {
            in_d[ele.to]++;
        }

        rep(i, v) if (in_d[i] == 0) {
                st.push(i);
            }
        vl ans; // ソート後のグラフ

        while (!st.empty()) {
            ll i = st.top();
            st.pop();
            ans.emplace_back(i);
            for (const auto j: table[i]) {
                ll to = j.to;
                in_d[to]--;
                if (in_d[to] == 0) st.push(to);
            }
        }

        return move(ans);
    }

    // O(v + e)
    // 閉路が存在するときは-1を返す
    ll get_longest_path() {
        vector<bool> seen(v), calc(v);
        vl d(v, 0);
        auto dfs = [&](auto &&f, ll now) -> ll {
            if (seen[now]) {
                if (!calc[now]) return -1;
                return d[now];
            }
            seen[now] = true;
            d[now] = 1;
            for (const auto &t: table[now]) {
                ll res = f(f, t.to);
                if (res == -1) return -1;
                d[now] = max(res + t.cost, d[now]);
            }
            calc[now] = true;
            return d[now];
        };

        ll ans = 0;
        rep(i, v) {
            ll tmp = dfs(dfs, i);
            if (tmp == -1) {
                return -1;
            }
            ans = max(tmp, ans);
        }
        return ans;
    }

    // 負の重みがあっても動く
    // O(v * e)
    vl bellman_ford(ll s) {
        d[s] = 0;
        rep(_, v - 1) {
            for (const auto &e1 : edges) {
                if (d[e1.from] != ll_inf && d[e1.to] > d[e1.from] + e1.cost) {
                    d[e1.to] = d[e1.from] + e1.cost;
                }
            }
        }
        return d;
    }

    // 各頂点までに到達する経路中に負の閉路が存在するか
    // bellman_fortで最短経路を出した後に使う
    vl has_negative_loop_in_path() {
        vl has_loop(v, false);
        rep(_, v - 1) {
            for (const auto &e1 : edges) {
                if (d[e1.to] > d[e1.from] + e1.cost) {
                    has_loop[e1.from] = true;
                }
                if (has_loop[e1.from]) {
                    has_loop[e1.to] = true;
                }
            }
        }
        return move(has_loop);
    }

    // O(e * logv)
    vl dijkstra(ll s) {
        // pairを使っているのは、比較関数を利用するため
        priority_queue<P, vector<P>, greater<>> que;
        d[s] = 0;
        que.push(P(0, s));

        while (!que.empty()) {
            P p = que.top();
            que.pop();
            ll min_v = p.second;
            if (d[min_v] < p.first) continue;
            for (const auto &ele: table[min_v]) {
                if (d[ele.to] > d[min_v] + ele.cost) {
                    d[ele.to] = d[min_v] + ele.cost;
                    pre[ele.to] = min_v;
                    que.push(P(d[ele.to], ele.to));
                }
            }
        }
        return d;
    }

    // 頂点tへの最短路
    // dijkstraで最短経路を出した後に使う
    vector<int> get_path(int t) {
        vector<int> path;
        for (; t != -1; t = pre[t]) path.emplace_back(t); // tがsになるまでpre[t]をたどっていく
        reverse(path.begin(), path.end());
        return move(path);
    }

    // 頂点sからの閉路検出
    // もし閉路がないならば v + 1の大きさのベクトルを返す。
    vl cyclic(ll s) {
        queue<ll> que;
        que.push(s);
        pre.assign(v, -1);
        d.assign(v, ll_inf);
        d[s] = 0;
        while (!que.empty()) {
            ll now = que.front();
            que.pop();
            each(e, table[now]) {
                if (d[e.to] != ll_inf) continue;
                d[e.to] = d[e.from] + 1;
                pre[e.to] = e.from;
                que.push(e.to);
            }
        }

        P best(ll_inf, -1);
        rep(i, v) {
            if (i == s) continue;
            each(e, table[i]) {
                if (e.to == s) {
                    best = min(best, {d[e.from], e.from});
                }
            }
        }
        if (best.first == ll_inf) return vl(v + 1);
        vl ret;
        ll now = best.second;
        while (now != -1) {
            ret.emplace_back(now + 1);
            now = pre[now];
        }
        return ret;
    }

    // 二部グラフ判定
    bool bipartite() {
        vl ids(v, -1);
        auto dfs = [&](auto &&f, ll s, ll id) -> bool {
            if (ids[s] == -1) {
                ids[s] = id;
            } else {
                return ids[s] != id;
            }
            each(e, table[s]) {
                if (f(f, e.to, 1 - id)) return true;
            }
            return false;
        };
        return dfs(dfs, 0, 1);
    };
};

class UnionFind {
private:
    vector<ll> size; // グループに属する物の数．
    vector<ll> d;
public:
    vector<ll> par; // 親
    vector<ll> rank; // 木の深さ
    ll groupCount;

    explicit UnionFind(unsigned int n) {
        par.resize(n);
        rank.resize(n);
        size.resize(n);
        groupCount = n;
        rep(i, n) {
            par[i] = i;
            rank[i] = 0;
            size[i] = 1;
            d[i] = 0;
        }
    }

    // 木の根を求める
    ll find(ll x) {
        if (par[x] == x) {
            return x;
        } else {
            return par[x] = find(par[x]);
        }
    }

    // グループのサイズを求める．
    ll calc_size(ll x) {
        return size[find(x)];
    }

    // xとyの属する集合を併合
    void unite(ll x, ll y) {
        x = find(x);
        y = find(y);
        if (x == y) return;
        groupCount--;
        if (rank[x] < rank[y]) {
            par[x] = y;
        } else {
            par[y] = x;
            if (rank[x] == rank[y]) rank[x]++;
        }
        size[x] = size[y] = size[x] + size[y];
    }

    // xとyが同じ集合に属するか否か
    bool is_same(ll x, ll y) {
        return find(x) == find(y);
    }
};


void solve() {
    ll n, m;
    cin >> n >> m;
    ve es;
    rep(i, m) {
        ll l, r, c;
        cin >> l >> r >> c;
        l--, r--;
        es.eb(edge(l, r, c));
    }
    sort(all(es), [](edge &a, edge &b) { return a.cost < b.cost; });

    UnionFind uf(n);
    Graph g(n);
    set<ll> idxs;
    rep(i, n) idxs.insert(i);
    rep(i, m) {
        if (idxs.empty()) break;
        ll s, t, c;
        s = es[i].from, t = es[i].to, c = es[i].cost;
        if (!uf.is_same(s, t)) {
            g.add(uf.find(s), uf.find(t), c);
        }
        auto itr = idxs.upper_bound(s);
        if (itr == idxs.end()) continue;
        ll now = *itr;
        itr++;

        while (itr != idxs.end() && *itr <= t) {
            uf.unite(now, *itr);
            g.add(s, *itr, c);
            idxs.erase(itr++);
        }
        g.add(uf.find(s), uf.find(now), c);
    }
    vl ans = g.dijkstra(0);
    cout << ans.back() << '\n';
}

int main() {
#ifdef MY_DEBUG
    while (true) {
#endif
        solve();
#ifdef MY_DEBUG
    }
#endif
    return 0;
}