Go: package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
//	"strings"
    "sort"
//    "container/heap"
)

/*********** Input **************/
/*
var rdr = bufio.NewReaderSize(os.Stdin, 1000000)

func readLine() string {
	buf := make([]byte, 0, 1000000)
	for {
		l, p, e := rdr.ReadLine()
		if e != nil {
			panic(e)
		}
		buf = append(buf, l...)
		if !p {
			break
		}
	}
	return string(buf)
}*/

func scanInt(sc *bufio.Scanner) int {
    sc.Scan()
    v, _ := strconv.Atoi(sc.Text())
    return v
}
/**************  End Input  **************/

func toInt(str string) int {
	n, _ := strconv.Atoi(str)
	return n
}

type Edge struct {
	to      int
	newTime int
}

type State struct {
	node int
	time int
}


/**************  Cell  *****************/

type Cell struct {
	item *State
	next *Cell
}

func NewCell(x *State, cp *Cell) *Cell {
	newCp := new(Cell)
	newCp.item = x
	newCp.next = cp
	return newCp
}

/************  End Cell  ***************/

/**************  Queue  ****************/

type Queue struct {
	size  int
	front *Cell
	rear  *Cell
}

func NewQue() *Queue {
	return new(Queue)
}

func (q *Queue) Push(x *State) {
	newNode := NewCell(x, nil)
	if q.size == 0 {
		q.front = newNode
		q.rear = newNode
	} else {
		q.rear.next = newNode
		q.rear = newNode
	}
	q.size++
}

func (q *Queue) PopLeft() (*State, bool) {
	if q.size == 0 {
		return nil, false
	} else {
		x := q.front.item
		q.front = q.front.next
		q.size--
		if q.size == 0 {
			q.rear = nil
		}
		return x, true
	}
}

func (q *Queue) IsEmpty() bool {
	return q.size == 0
}

/***********  end Queue  ***********/

type EdgeSorter struct {
    edges []Edge
    lessfunc []lessFunc
    used int
    length int
}

type lessFunc func(i, j *Edge)bool

func (ES EdgeSorter) Len() int {
    return len(ES.edges)
}

func (ES EdgeSorter) Swap(i, j int) {
    ES.edges[i], ES.edges[j] = ES.edges[j], ES.edges[i]
}

func (ES EdgeSorter) Less(i, j int) bool {
    var k int
    p, q := &ES.edges[i], &ES.edges[j]
    for k=0; k<len(ES.lessfunc)-1;k++ {
        less := ES.lessfunc[k]
        switch {
            case less(p, q):
                return true
            case less(q, p):
                return false
        }
    }
    return ES.lessfunc[k](p, q)
}

/*********** PriorityQueue ***********/
/*
type PriorityQueue struct {
    arr []*State
    size int
}

func (pq PriorityQueue) Len() int {
    return len(pq.arr)
}

func (pq PriorityQueue) Swap(i, j int) {
    pq.arr[i], pq.arr[j] = pq.arr[j], pq.arr[i]
}

func (pq PriorityQueue) Less(i, j int) bool{
    return pq.arr[i].time < pq.arr[j].time
}

func (pq *PriorityQueue) Push(x interface{}) {
    item := x.(*State)
    pq.arr = append(pq.arr, item)
    pq.size++
}

func (pq *PriorityQueue) Pop() interface{} {
    n := pq.size
    item := pq.arr[n - 1]
    pq.arr = pq.arr[:n - 1]
    pq.size--
    return item
}
*/


func main() {
    sc := bufio.NewScanner(os.Stdin)
    sc.Split(bufio.ScanWords)
	n, m := scanInt(sc), scanInt(sc)
	edges := make([]EdgeSorter, 0)
	for i := 0; i < n; i++ {
		edges = append(edges, EdgeSorter{})
	}
	for i := 0; i < m; i++ {
        a, b, c := scanInt(sc) - 1, scanInt(sc) - 1, scanInt(sc)
		edges[a].edges = append(edges[a].edges, Edge{to: b, newTime: c})
	}

    byNewTime := func(p1 *Edge, p2 *Edge) bool{
        return p1.newTime >= p2.newTime
    }

    for i, _ := range edges {
        v := &edges[i]
        v.lessfunc = append(v.lessfunc, byNewTime)
        sort.Sort(v)
        v.used = 0
        v.length = len(v.edges)
    }
    var minTime [100000]int
    for i := 0; i < n; i++ {
        minTime[i] = 2000000000
    }
    minTime[0] = 0
    que := Queue{}
    que.Push(&State{node: 0, time: 0})
	ans := -1
	for !que.IsEmpty() {
		state, _ := que.PopLeft()
		for edges[state.node].used < edges[state.node].length {
            e := edges[state.node].edges[edges[state.node].used]
			if e.newTime >= state.time {
                if e.to == n - 1 && e.newTime > ans {
                    ans = e.newTime
             

C++: #include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<utility>
#include<map>
#include<complex>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int> P;
typedef pair<ll, ll> LP;
typedef vector<int> vec;
#define rep(i,n) for(int i=0;i<n;i++)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define stop char nyaa;cin>>nyaa;
#define per(i,n) for(int i=n-1;i>=0;i--)

//geometry
typedef long double ld;
typedef complex<ld> Point;
const ld pi = acos(-1.0);
ld dot(Point a, Point b) { return real(conj(a)*b); }
ld cross(Point a, Point b) { return imag(conj(a)*b); }

struct Line {
	Point a, b;
};

Point is_ll(Line s, Line t) {
	Point sv = s.b - s.a;
	Point tv = t.b - t.a;
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}

vector<int> ans;
//scc
struct graph {
private:
	int n; vector<vector<int>> G, rG;
	vector<bool> used;
	vector<int> vs;

	int mk;
	vector<vector<int>> fG;
	vector<vector<int>> ori;
	vector<int> trans;
public:
	graph(int sz) {
		n = sz;
		G.resize(n);
		rG.resize(n);
		used.resize(n);

		fG.resize(n);
		trans.resize(n, -1);
		ori.resize(n);
	}
	void add_edge(int a, int b) {
		G[a].push_back(b);
		rG[b].push_back(a);
	}
	void dfs(int v) {
		used[v] = true;
		rep(i, G[v].size()) {
			if (!used[G[v][i]])dfs(G[v][i]);
		}
		vs.push_back(v);
	}
	void rdfs(int v, int k) {
		used[v] = true;
		queue<int> q;
		q.push(v);
		vector<int> c;
		while (!q.empty()) {
			int id = q.front(); q.pop();
			ori[k].push_back(id);
			rep(j, rG[id].size()) {
				int to = rG[id][j];
				if (used[to]) {
					if (trans[to] >= 0)c.push_back(trans[to]);
					continue;
				}
				used[to] = true; q.push(to);
			}
		}
		sort(c.begin(), c.end());
		int len = unique(c.begin(), c.end()) - c.begin();
		rep(i, len) {
			fG[c[i]].push_back(k);
		}
		rep(i, ori[k].size()) {
			trans[ori[k][i]] = k;
		}
	}
	void scc() {
		fill(used.begin(), used.end(), false);
		rep(i, n)if (!used[i])dfs(i);
		fill(used.begin(), used.end(), false);
		int k = 0;
		per(i, (int)vs.size()) {
			if (!used[vs[i]]) {
				rdfs(vs[i], k); k++;
			}
		}
		mk = k;
	}
	void query(vector<int> &x,int &cur,int &sz) {
		vector<int> c(mk, 0);
		vector<int> val(mk, -1);
		rep(i, mk) {
			rep(j, ori[i].size()) {
				val[i] = max(val[i], ans[x[ori[i][j]]]);
				if (x[ori[i][j]] == sz - 1) {
					val[i] = max(val[i], cur);
				}
			}
		}
		rep(i, mk) {
			rep(j, fG[i].size()) {
				int to = fG[i][j];
				c[to]++;
			}
		}
		queue<int> q;
		rep(i, mk)if (c[i] == 0)q.push(i);
		while (!q.empty()) {
			int id = q.front(); q.pop();
			rep(j, fG[id].size()) {
				int to = fG[id][j]; c[to]--;
				if (c[to] == 0) {
					q.push(to);
				}
				val[to] = max(val[to], val[id]);
			}
		}
		rep(i, mk) {
			rep(j, ori[i].size()) {
				if (x[ori[i][j]] == sz - 1)continue;
				ans[x[ori[i][j]]] = val[i];
			}
		}
	}
};


void to_unique(vector<int> &v) {
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
}
void solve() {
	int n, m; cin >> n >> m;
	ans.resize(n, -1);
	vector<pair<int, P>> v;
	rep(i, m) {
		int a, b, c; cin >> a >> b >> c;
		a--; b--;
		if (a == n - 1)continue;
		v.push_back({ c,{ b,a } });
	}
	sort(v.begin(), v.end(), greater<pair<int,P>>());
	rep(i, v.size()) {
		int le = i;
		while (i + 1 < (int)v.size() && v[i].first == v[i + 1].first) {
			i++;
		}
		if (i == le) {
			int id = v[i].second.first; int to = v[i].second.second;
			ans[to] = max(ans[to], ans[id]);
			if (id == n - 1)ans[to] = max(ans[to], v[i].first);
			continue;
		}
		vector<int> x;
		vector<P> u;
		Rep(j, le, i+1) {
			u.push_back(v[j].second);
			x.push_back(v[j].second.first);
			x.push_back(v[j].second.second);
		}
		to_unique(x);
		{
			map<int, int> mp;
			rep(j, x.size()) {
				mp[x[j]] = j;
			}
			rep(j, u.size()) {
				u[j].first = mp[u[j].first];
				u[j].second = mp[u[j].second];
			}
		}
		graph g(x.size());
		rep(j, u.size()) {
			//cout << x[u[j].first] << " " << x[u[j].second] << endl;
			g.add_edge(u[j].first, u[j].second);
		}
		g.scc(); g.query(x,v[i].first,n);
	}
	//rep(i, n)cout << ans[i] << endl;
	cout << ans[0] << endl;
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	solve();
	//stop
		return 0;
}