#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
// Số lượng nút tối đa: N ban đầu + M nút mới (nội bộ) <= 400005
const int MAX_NODES = 400005;
const int LOG_MAX = 20;
// ------------------- DSU (Union-Find) -------------------
int parent_dsu[MAX_NODES];
int find_set(int v) {
if (v == parent_dsu[v])
return v;
return parent_dsu[v] = find_set(parent_dsu[v]);
}
// ------------------- Cây Kruskal và Binary Lifting -------------------
int current_node;
int up[MAX_NODES][LOG_MAX];
long long node_cost[MAX_NODES];
int depth[MAX_NODES];
void init(int n) {
for (int i = 1; i <= n; ++i) {
parent_dsu[i] = i;
depth[i] = 0;
up[i][0] = 0;
node_cost[i] = 0;
}
// Khởi tạo các nút có thể được tạo ra
for (int i = n + 1; i < MAX_NODES; ++i) {
up[i][0] = 0;
depth[i] = 0;
}
current_node = n;
}
void union_sets(int u, int v, long long w) {
int root_u = find_set(u);
int root_v = find_set(v);
if (root_u != root_v) {
current_node++;
int new_root = current_node;
// 1. DSU: Hợp nhất
parent_dsu[root_u] = new_root;
parent_dsu[root_v] = new_root;
parent_dsu[new_root] = new_root;
// 2. Cây Kruskal: Trọng số và Độ sâu
node_cost[new_root] = w;
depth[new_root] = max(depth[root_u], depth[root_v]) + 1;
// 3. Binary Lifting: Cập nhật nút cha
up[root_u][0] = new_root;
up[root_v][0] = new_root;
// Tiền xử lý các bước nhảy lớn hơn cho nút mới
for (int k = 1; k < LOG_MAX; ++k) {
up[new_root][k] = up[up[new_root][k - 1]][k - 1];
}
}
}
// Hàm tìm LCA
int find_lca(int u, int v) {
// 1. Đảm bảo u là nút sâu hơn
if (depth[u] < depth[v]) swap(u, v);
// 2. Đưa u lên cùng độ sâu với v
for (int k = LOG_MAX - 1; k >= 0; --k) {
if (up[u][k] != 0 && depth[u] - (1 << k) >= depth[v]) {
u = up[u][k];
}
}
if (u == v) return u;
// 3. Nhảy lên từ u và v đồng thời
for (int k = LOG_MAX - 1; k >= 0; --k) {
// Chỉ nhảy nếu nút cha khác nhau VÀ không phải nút 0 (null)
if (up[u][k] != up[v][k] && up[u][k] != 0) {
u = up[u][k];
v = up[v][k];
}
}
// 4. LCA là nút cha (tổ tiên 2^0) của u (hoặc v) hiện tại
return up[u][0];
}
long long query_min_max_cost(int s, int t) {
// 1. Kiểm tra tính liên thông: Nếu gốc DSU khác nhau, chưa thể đi
if (find_set(s) != find_set(t)) {
return -1;
}
// 2. Kiểm tra trường hợp đặc biệt: Nếu s và t đã được kết nối
// nhưng chưa có nút nội bộ nào được tạo ra (tức là chỉ có các nút lá)
// Trường hợp này không nên xảy ra nếu find_set(s) == find_set(t)
// và thuật toán đã chạy đúng.
// 3. Tìm LCA
int lca = find_lca(s, t);
// 4. Kết quả là giá trị tại nút LCA
// Nếu LCA là 0 (điều kiện này chỉ xảy ra nếu s và t không liên thông hoặc
// lca là nút lá, nhưng lca phải là nút nội bộ nếu s != t và đã liên thông)
if (lca == 0) return -1; // Fallback an toàn, mặc dù về mặt lý thuyết lca > n.
return node_cost[lca];
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, m;
if (!(cin >> n >> m)) return 0;
init(n);
for (int i = 0; i < m; ++i) {
int u, v, s, t;
long long w;
if (!(cin >> u >> v >> w >> s >> t)) break;
// 1. Thêm cạnh
union_sets(u, v, w);
// 2. Truy vấn
cout << query_min_max_cost(s, t) << "\n";
}
return 0;
}