观察到要求“路径上最大可能边权”,所以考虑 Kruskal 重构树。
然后一个询问相当于是求 $x$ 点和所有白点的 lca 的点权。
一个经典结论是:点集的 lca 相当于 dfn 最小和 dfn 最大的两点 lca,于是就干完了。
直接拿线段树维护白点的最大最小 dfn 即可。
甚至写完直接过编译,补了一个 w 数组预处理就过了。
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 15, M = N << 1;
int n, n1, q;
struct edges {
int u, v, w;
} edge[N];
bool cmp(edges a, edges b) { return a.w < b.w; }
int p[N]; //Union Find
int find(int x) { return (p[x] == x) ? x : p[x] = find(p[x]); }
int h[N], e[M], ne[M], idx = 0; //Kruskal 重构树
int w[N];
void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
int fa[N], dep[N], sz[N], son[N];
void dfs1(int u, int father) {
fa[u] = father;
dep[u] = dep[father] + 1, sz[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == father) continue;
dfs1(v, u);
sz[u] += sz[v];
if (!son[u] || (sz[son[u]] < sz[v])) son[u] = v;
}
}
int dfn[N], top[N], tot = 0;
void dfs2(int u, int t) {
dfn[u] = ++tot, top[u] = t;
if (son[u]) dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int lca(int u, int v) {
if (!u || !v) return 0;
if (u == v) return u;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
return u;
}
int getmx(int a, int b) {
if (!a || !b) return a | b;
return (dfn[a] > dfn[b]) ? a : b;
}
int getmn(int a, int b) {
if (!a || !b) return a | b;
return (dfn[a] < dfn[b]) ? a : b;
}
struct Tree {
int l, r;
int mn, mx; //区间内的 dfn 最小最大
int Min, Max; //区间内选中的白点 dfn 最小最大
int flag; //0 无标记;1 白点;2 黑点
} tr[N << 2];
void pushup(int u) {
tr[u].Min = getmn(tr[u << 1].Min, tr[u << 1 | 1].Min);
tr[u].Max = getmx(tr[u << 1].Max, tr[u << 1 | 1].Max);
}
void pushdown(int u) {
if (tr[u].flag == 1) {
tr[u << 1].flag = tr[u << 1 | 1].flag = 1;
tr[u << 1].Min = tr[u << 1].mn, tr[u << 1].Max = tr[u << 1].mx;
tr[u << 1 | 1].Min = tr[u << 1 | 1].mn, tr[u << 1 | 1].Max = tr[u << 1 | 1].mx;
tr[u].flag = 0;
} else if (tr[u].flag == 2) {
tr[u << 1].flag = tr[u << 1 | 1].flag = 2;
tr[u << 1].Min = 0, tr[u << 1].Max = 0;
tr[u << 1 | 1].Min = 0, tr[u << 1 | 1].Max = 0;
tr[u].flag = 0;
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
tr[u].Min = tr[u].Max = 0;
tr[u].flag = 0;
if (l == r) {
tr[u].mn = tr[u].mx = l;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
tr[u].mn = getmn(tr[u << 1].mn, tr[u << 1 | 1].mn); // init
tr[u].mx = getmx(tr[u << 1].mx, tr[u << 1 | 1].mx); // init
}
void change1(int u, int l, int r) { // 白点
if (l > r || tr[u].r < l || tr[u].l > r) return;
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].Min = tr[u].mn, tr[u].Max = tr[u].mx;
tr[u].flag = 1;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) change1(u << 1, l, r);
if (r > mid) change1(u << 1 | 1, l, r);
pushup(u);
}
void change2(int u, int l, int r) { // 黑点
if (l > r || tr[u].r < l || tr[u].l > r) return;
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].Min = 0, tr[u].Max = 0;
tr[u].flag = 2;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) change2(u << 1, l, r);
if (r > mid) change2(u << 1 | 1, l, r);
pushup(u);
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i < n; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
sort(edge + 1, edge + n, cmp);
n1 = n, idx = 0;
for (int i = 1; i <= n * 2; i++) p[i] = i, h[i] = -1, w[i] = -1;
for (int i = 1; i < n; i++) {
int u = edge[i].u, v = edge[i].v;
int fu = find(u), fv = find(v);
if (fu == fv) continue;
n1++, p[fu] = n1, p[fv] = n1, w[n1] = edge[i].w;
add(n1, fu), add(fu, n1);
add(n1, fv), add(fv, n1); //Kruskal 重构树
}
dfs1(n1, 0), dfs2(n1, n1);
build(1, 1, n);
while (q--) {
int opt; scanf("%d", &opt);
if (opt == 1) {
int l, r; scanf("%d%d", &l, &r);
change1(1, l, r);
} else if (opt == 2) {
int l, r; scanf("%d%d", &l, &r);
change2(1, l, r);
} else {
int x; scanf("%d", &x);
int l = lca(tr[1].Min, tr[1].Max);
l = lca(x, l);
if (!l) puts("-1");
else printf("%d\n", w[l]);
}
}
return 0;
}