A. P11364 树上查询 (7)
一开始想着直接转 dfn 好处理一个区间的 LCA,因为编号区间可以直接变成区间最大最小 dfn 中最小 dep。但是若是想要维护,很难不上一些笛卡尔树和单调栈。玩一玩想一想发现根本想不到一个性质优秀的刻画方式,也想不到 dfn 怎么很好的跟编号对应。不过由于 dfn 求 LCA 的性质,可以发现可以把区间内 dfn 相邻 / 有交 的两个区间并起来,并起来的答案是小区间 dep 最小值取 min。所以考虑拆分极值对应大区间,比如可以把所有 dfn 相邻的中间区间 最小 dep 取 min。虽然还是没法直接维护。
但是反思一下,为什么要转 dfn。既然可以并起来,那我是不是直接拿编号相邻的并起来也可以?毕竟可以把区间内对应 dfn 当成一个点,然后如此拆分相当于用一条链串起来,很明显只要我能串到极值点就一定可以算出真正答案。因此,令 d[i] = dep[LCA(i, i + 1)],就有区间 \([l,r]\) 的 LCA 深度为 \(\min\limits_{i\in[l,r)} d_i\)。有了这个,再考虑对于每个 \(i\) 求出极长区间 \([L_i,R_i]\) 使得 dep[LCA] = d[i]。考虑如何求解:
此时相当于每次查询一个 \([l,r]\),求满足 \(|[L_i,R_i]\cap [l,r]|\geq k\) 的最大 \(d_i\)。此时相当于要求 \(R_i\geq l+k-1,L_i\leq r-k+1, R_i-L_i+1\geq k\)。首先已经可以 三维偏序 俩 log 做(没想到,qjy 教的,跑的很快。)但是如果想要直接维护,会发现 \(L_i\) 和 \(R_i\) 的互相限制很令人头疼,不难发现其实这三个限制对应的是边界相交的三种情况,考虑怎么拆分一下。可以改成,若 \(R_i\geq r\),则需要 \(L_i\leq r-k+1\);若 \(R_i< r\),则需要 \(R_i\geq l+k-1\),且 \(R_i-L_i+1\geq k\)。前者可以 按 \(r\) 扫描线,后者按区间长扫描线即可。
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <cassert>
#include <iostream>
#include <numeric>
#include <vector>using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 5e5 + 1;int n, q;
vector<int> e[kN];
int dfn[kN], dfc, dep[kN], st[20][kN];
void Init(int x, int fa) {st[0][dfn[x] = ++dfc] = fa;dep[x] = dep[fa] + 1;for (auto v : e[x])(v != fa) && (Init(v, x), 0);
}
inline int High(int x, int y) { return dep[x] < dep[y] ? x : y; }
inline int LCA(int x, int y) {if (x == y)return x;x = dfn[x], y = dfn[y];x > y && (swap(x, y), 0);int k = __lg(y - x);return High(st[k][x + 1], st[k][y - (1 << k) + 1]);
}int t[4 * kN];
void Update(int p, int w, int x = 1, int l = 1, int r = n) {if (l == r)return t[x] = max(t[x], w), void();int mid = (l + r) / 2;if (p <= mid)Update(p, w, 2 * x, l, mid);elseUpdate(p, w, 2 * x + 1, mid + 1, r);t[x] = max(t[2 * x], t[2 * x + 1]);
}
int Max(int L, int R, int x = 1, int l = 1, int r = n) {if (R < l || r < L)return 0;else if (L <= l && r <= R)return t[x];int mid = (l + r) / 2;return max(Max(L, R, 2 * x, l, mid), Max(L, R, 2 * x + 1, mid + 1, r));
}int f[kN];
struct kItv { int l, r, w; };
vector<kItv> krg[kN];
struct rItv { int l, w; };
vector<rItv> rrg[kN];
inline void GetItv() {vector<int> v = {0};for (int i = 1; i < n; i++) {f[i] = dep[LCA(i, i + 1)];for (; v.size() > 1 && f[v.back()] > f[i]; v.pop_back()) {int l = v[v.size() - 2] + 1, r = i;krg[r - l + 1].push_back({l, r, f[v.back()]});rrg[r].push_back({l, f[v.back()]});}v.push_back(i);}for (; v.size() > 1; v.pop_back()) {int l = v[v.size() - 2] + 1, r = n;krg[r - l + 1].push_back({l, r, f[v.back()]});rrg[r].push_back({l, f[v.back()]});}for (int i = 1; i <= n; i++) {krg[1].push_back({i, i, dep[i]});rrg[i].push_back({i, dep[i]});}
}int ans[kN];
struct kQuery { int l, r, i; };
vector<kQuery> kq[kN];
struct rQuery { int l, k, i; };
vector<rQuery> rq[kN];
int main() {
#ifndef ONLINE_JUDGEfreopen("input.in", "r", stdin);freopen("output.out", "w", stdout);
#endifcin.tie(0)->sync_with_stdio(0);cin >> n;for (int i = 1, u, v; i < n; i++) {cin >> u >> v;e[u].push_back(v), e[v].push_back(u);}Init(1, 0);for (int d = 1; d < 20; d++) {for (int i = 1; i + (1 << d) - 1 <= n; i++)st[d][i] = High(st[d - 1][i], st[d - 1][i + (1 << d - 1)]);}GetItv();cin >> q;for (int i = 1, l, r, k; i <= q; i++) {cin >> l >> r >> k;kq[k].push_back({l, r, i});rq[r].push_back({l, k, i});}for (int len = n; len >= 1; len--) {for (auto [l, r, w] : krg[len])Update(r, w);for (auto [l, r, i] : kq[len])ans[i] = Max(l + len - 1, r - 1);}fill_n(t + 1, 4 * n, 0);for (int r = n; r >= 1; r--) {for (auto [l, w] : rrg[r])Update(l, w);for (auto [l, k, i] : rq[r])ans[i] = max(ans[i], Max(1, r - k + 1));}for (int i = 1; i <= q; i++)cout << ans[i] << '\n';return 0;
}