QTREE5

· · 题解

一个只需要树剖的 O(nlog^3n) 做法,但常数比 LCT 小所以跑得不慢,可以通过,且实现难度小于 LCT。

树上两点间最短距离为 depth_u+depth_v-2 \times depth_{lca(u, v)},因此思路是对于每个点 u 维护

直接树剖建线段树维护。当一个点的颜色变化时需要将其到根的路径进行对应修改。黑转白时用其深度更新,白转黑时相当于要撤销转白。不难想到给每个节点开一个集合来带删维护最小值。把节点中的集合看作懒标记,由于删除操作均为撤销之前的某个添加操作,所有标记在被撤销时都出现在添加时的原位上,类似扫描线维护矩阵面积并的线段树。因此可以无需 `pushdown`,查询时类似标记永久化计算影响即可。实现细节见代码。 ```cpp #include<bits/stdc++.h> #define ep emplace #define eb emplace_back using namespace std; const int MAXN = 1e5 + 10; int n, q; vector<int> to[MAXN]; struct Segment { int l, r; multiset<int> s; int min, depth; } a[MAXN * 4]; int pa[MAXN], depth[MAXN], siz[MAXN], hson[MAXN]; int top[MAXN], dfn[MAXN], rk[MAXN], timecnt; int co[MAXN]; #define ls (p << 1) #define rs (p << 1 | 1) #define mid ((a[p].l + a[p].r) >> 1) void pushup(int p) { a[p].min = 1e6; if (a[p].l != a[p].r) a[p].min = min(a[ls].min, a[rs].min), a[p].depth = max(a[ls].depth, a[rs].depth); if (a[p].s.size()) a[p].min = min(a[p].min, *a[p].s.begin() - 2 * a[p].depth); return; } void build(int p, int l, int r) { a[p].l = l, a[p].r = r; if (l == r) { a[p].depth = depth[rk[l]]; a[p].min = 1e6; return; } build(ls, l, mid), build(rs, mid + 1, r); pushup(p); return; } void modify(int p, int l, int r, int k, bool t) { if (l <= a[p].l && a[p].r <= r) { if (!t) a[p].s.ep(k); else a[p].s.erase(a[p].s.find(k)); pushup(p); return; } if (l <= mid) modify(ls, l, r, k, t); if (mid < r) modify(rs, l, r, k, t); pushup(p); return; } int query(int p, int l, int r) { if (l <= a[p].l && a[p].r <= r) return a[p].min; int res = 1e6; if (a[p].s.size()) res = min(res, *a[p].s.begin() - 2 * depth[rk[r]]); if (l <= mid) res = min(res, query(ls, l, r)); if (mid < r) res = min(res, query(rs, l, r)); return res; } #undef ls #undef rs #undef mid void buildtree(int x, int parent) { pa[x] = parent, depth[x] = depth[pa[x]] + 1, siz[x] = 1; for (int y : to[x]) { if (y == pa[x]) continue; buildtree(y, x), siz[x] += siz[y]; if (siz[y] > siz[hson[x]]) hson[x] = y; } return; } void decompose(int x, int t) { top[x] = t, dfn[x] = ++timecnt, rk[timecnt] = x; if (hson[x]) decompose(hson[x], t); for (int y : to[x]) if (!dfn[y]) decompose(y, y); return; } void modifypath(int x, int t) { int d = depth[x]; while (x) { modify(1, dfn[top[x]], dfn[x], d, t); x = pa[top[x]]; } return; } signed main() { ios:: sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n; for (int i = 1; i < n; ++i) { int x, y; cin >> x >> y; to[x].eb(y), to[y].eb(x); } buildtree(1, 0), decompose(1, 1), build(1, 1, n); cin >> q; while (q--) { int op, x; cin >> op >> x; if (!op) { modifypath(x, co[x]); co[x] = !co[x]; } else { if (a[1].min > n) { cout << -1 << "\n"; continue; } int d = depth[x]; int res = 1e6; while (x) { res = min(res, d + query(1, dfn[top[x]], dfn[x])); x = pa[top[x]]; } cout << res << "\n"; } } return 0; } ```