#include #define rep(i,n) for(int i = 0; i < (n); i++) using namespace std; typedef long long ll; class union_find { public: union_find(int n) : data(n, -1) {} int unite(int x, int y) { x = root(x), y = root(y); if(x != y) { if(size(x) < size(y)) swap(x, y); data[x] += data[y]; return data[y] = x; } return -1; } int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); } int size(int x) { return -data[root(x)]; } bool same(int x, int y) { return root(x) == root(y); } private: vector data; }; vector< int > bfs(vector> &graph, int s) { int INF = numeric_limits::max(); vector dist(graph.size(), INF); queue q; dist[s] = 0, q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); if(u != dist[u]) continue; for(int v : graph[u]) if(dist[v] == INF) dist[v] = dist[u] + 1, q.push(v); } return dist; } int main(){ cin.tie(0); ios::sync_with_stdio(0); int n,m; cin >> n >> m; vector> es(m); for(auto &[s, t, d] : es) { cin >> s >> t >> d; s--; t--; } int w_max = [&]() { int ok = 0, ng = 1e9+1; while(ng - ok > 1) { int mid = (ok + ng) / 2; ([&](int w) { union_find uf(n); for(auto [s, t, d] : es) if(w <= d) uf.unite(s, t); return uf.same(0, n - 1); }(mid) ? ok : ng) = mid; } return ok; }(); vector> G(n); for(auto [s, t, d] : es) if(w_max <= d) { G[s].push_back(t); G[t].push_back(s); } cout << w_max << " " << bfs(G, 0)[n - 1] << endl; }