#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 2e5 + 10;
const int LOG = 20; // 2^20 > 2e5,足够覆盖所有节点深度
// 并查集(Kruskal用,路径压缩+按秩合并)
struct DSU {
vector<int> fa, siz;
DSU(int n) {
fa.resize(n + 1);
siz.resize(n + 1, 1);
for (int i = 1; i <= n; i++) fa[i] = i;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int u, int v) {
u = find(u), v = find(v);
if (u == v) return false;
if (siz[v] > siz[u]) swap(u, v);
fa[v] = u;
siz[u] += siz[v];
return true;
}
bool same(int u, int v) {
return find(u) == find(v);
}
};
// 边的结构体(Kruskal排序用)
struct Edge {
int u, v, w;
bool operator<(const Edge& b) const {
return w < b.w;
}
};
// MST的邻接表:pair<邻接点, 边权>
vector<pair<int, int>> g[MAXN];
// 倍增数组:up[k][u] = u的2^k级祖先,maxw[k][u] = u到2^k级祖先的最大边权
int up[LOG][MAXN];
int maxw[LOG][MAXN];
// 节点深度
int dep[MAXN];
int n, m, q;
// 深搜预处理倍增数组(从根节点1开始,可任选根)
void dfs(int u, int fa, int w) {
up[0][u] = fa; // 2^0级祖先 = 直接父节点
maxw[0][u] = w; // 到直接父节点的边权
dep[u] = dep[fa] + 1; // 深度=父节点深度+1
// 预处理2^1 ~ 2^(LOG-1)级祖先
for (int k = 1; k < LOG; k++) {
up[k][u] = up[k-1][up[k-1][u]];
maxw[k][u] = max(maxw[k-1][u], maxw[k-1][up[k-1][u]]);
}
// 遍历子节点
for (auto& e : g[u]) {
int v = e.first, val = e.second;
if (v != fa) {
dfs(v, u, val);
}
}
}
// 求a和b的LCA,并返回路径上的最大边权(最小瓶颈值)
int query(int a, int b) {
if (a == b) return 0; // 自身到自身,瓶颈值0
if (!DSU(n).same(a, b)) return -1; // 不连通,返回-1(实际可提前判断)
int res = 0;
// 先将a和b提到同一深度,同步记录最大边权
if (dep[a] < dep[b]) swap(a, b);
for (int k = LOG-1; k >= 0; k--) {
if (dep[up[k][a]] >= dep[b]) {
res = max(res, maxw[k][a]);
a = up[k][a];
}
}
if (a == b) return res;
// 同时上跳,直到找到LCA,同步记录最大边权
for (int k = LOG-1; k >= 0; k--) {
if (up[k][a] != up[k][b]) {
res = max(res, max(maxw[k][a], maxw[k][b]));
a = up[k][a];
b = up[k][b];
}
}
// 最后跳一步到LCA,加上最后两条边的权值
res = max(res, max(maxw[0][a], maxw[0][b]));
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}