用到的所有 trick 应该都是比较常用的?可以作为一道综合练习题。
该做法来自洛谷第一篇题解。
首先是经典引理:区间 lca 等于「区间相邻点 lca 」中深度最小的那个点(区间长度大于 \(1\))。
证明:
显然区间 \([l, r]\) 的 lca 深度小于等于上面的东西 (\(dep_{\operatorname{lca}(u, v)} \le dep_u\))。因此只需证明上面这个东西能取到区间 lca。
考虑区间 lca \(u\)。若 \(u\) 本身就在区间中,则显然能取到。否则:这个区间中的点一定可以按在 \(u\) 的哪个子树划分成严格大于 \(1\) 个集合。那么一定存在相邻两个点,它们不在同一集合中。那么它们的 lca 就是 u。
这里用了分析 lca 的一个经典技巧:\(x, y\) 一定位于 lca(x, y) 的两个不同子树中(不考虑 x,y 为祖先后代关系)。
判掉 \(k=1\) 的情况后,整个问题就完全成了一个序列问题:每次询问给定一个区间 \([l, r]\),求 \(\max_{[x, y] \subseteq [l, r] ∧ |[x, y]| = k} \left(\min_{i\in [x, y]}a_i \right)\)。也就是长度为 \(k\) 的子区间最小值的最大值。
这个问题形式就很简洁。下面处理这个问题的技巧则更精妙:
首先是一个称为支配对的思想:只考虑对答案有本质贡献的点对或其他什么东西,而如果这样的东西很少(比如说 \(O(n)\) 或 \(O(n\log n)\),比起最初的 \(O(n^2)\) 的点对),能够用数据结构维护,就做完了。
对于这个问题,定义支配对为区间 min 为某个值的极大区间。考虑区间 min 在哪个点处取到,可以发现这个东西就是这个点前面/后面第一个比它小的数形成的开区间。这个东西很好用单调栈维护。同时也说明了支配对不会超过 \(n\)。
现在我们提取出一堆带权(lca 深度/区间 min)支配区间。问题转化为:对于每个查询区间 \([ql, qr]\),求出和它交集大于等于 \(k\) 的所有区间的权的最大值。
我们分两类讨论:
对于 \(R \ge qr\) 的情况,限制为 \(R \ge qr ∧ L \le qr - k + 1\)(题目保证 \(k \le qr - ql + 1\))
k 只和 \(ql, qr\) 相关,第一个限制可以对 \(R\) 做扫描线,后面的限制转化为查询前缀 min,树状数组维护。
对于 \(R < qr\) 的情况,限制为 \(R - L + 1 \le k ∧ R \ge ql + k - 1 ∧ R < qr\)。第一个限制仍然扫描线,后面两个限制关于 \(R\) 是一段区间,线段树维护。
做完了,\(O(n\log n)\)!!!
template <typename T1, typename T2>
bool ckmax(T1& LHS, const T2& RHS) {return LHS < RHS ? LHS = RHS, true : false;
}
template <typename T1, typename T2>
bool ckmin(T1& LHS, const T2& RHS) {
const int N = 5e5 + 10;
int a[N], dep[N], b[N];
vi adj[N];
int n; namespace fenwick {//vi int t[N]; // int n;// fenwick() = default;// fenwick(int _n) {// n = _n; t.resize(n + 1);// }int lb(int x) {return x & (-x);}void add(int pos, int x) {while (pos <= n) {ckmax(t[pos], x); pos += lb(pos);}}int query(int pos) {int res = 0;while (pos) {ckmax(res, t[pos]); pos -= lb(pos);}return res;}
};namespace seg {int w[N << 2];#define ls (u << 1)#define rs (u << 1 | 1)#define mid ((l + r) >> 1)void pushup(int u) {ckmax(w[u], max(w[ls], w[rs]));}void update(int u, int l, int r, int pos, int x) {if (l == r) ckmax(w[u], x);else {if (pos <= mid) update(ls, l, mid, pos, x);else update(rs, mid + 1, r, pos, x);pushup(u);}}int query(int u, int l, int r, int ql, int qr) {if (ql <= l and r <= qr) return w[u];if (qr < l or r < ql) return 0;return max(query(ls, l, mid, ql, qr), query(rs, mid + 1, r, ql, qr));}
}namespace cornercase {int st[19][N];int Max(int l, int r) {int d = __lg(r - l + 1);return max(st[d][l], st[d][r - (1 << d) + 1]);}void init() {F (i, 1, n) st[0][i] = dep[i];F (i, 1, __lg(n)) {F (j, 1, n - (1 << i) + 1) {st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);}}}
}namespace LCA {int tot;int st[19][N], dfn[N];void dfs(int u, int fa) {dep[u] = dep[fa] + 1;dfn[u] = ++tot; st[0][tot] = fa;for (int v : adj[u]) {if (v == fa) continue;dfs(v, u);}}int Min(int u, int v) {return dfn[u] < dfn[v] ? u : v;}void init() {F (i, 1, __lg(n)) {F (j, 1, n - (1 << i) + 1) {st[i][j] = Min(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);}}}int lca(int u, int v) {if (u == v) return u;u = dfn[u], v = dfn[v];if (u > v) swap(u, v);u++;int d = __lg(v - u + 1);return Min(st[d][u], st[d][v - (1 << d) + 1]);}
} using LCA::lca;struct Query {int l, r, k, id;
} q[N]; int OoO;struct interval {int l, r, dep;
} h[N]; int ovo;int ans[N];void solve() {cin >> n;F (i, 1, n - 1) {int u, v; cin >> u >> v; adj[u].pb(v); adj[v].pb(u);}LCA::dfs(1, 0); LCA::init();// debug(pair<int*, int*>(dep + 1, dep + n + 1));cornercase::init();int Q; cin >> Q;F (i, 1, Q) {int l, r, k; cin >> l >> r >> k;if (k == 1) {debug(i);ans[i] = cornercase::Max(l, r);} else {q[++OoO] = {l, r - 1, k - 1, i};}}n--;F (i, 1, n) {b[i] = dep[lca(i, i + 1)];}vi stk(n + 1); int top = 0;vi L(n + 1), R(n + 1);F (i, 1, n) { while (top and b[stk[top]] >= b[i]) {top--;}L[i] = stk[top] + 1;stk[++top] = i;} top = 0;DF (i, n, 1) {while (top and b[stk[top]] >= b[i]) {top--;}R[i] = top ? stk[top] - 1 : n;stk[++top] = i;}// debug(pair<int*, int*>(b + 1, b + 1 + n));// F (i, 1, n) {// debug(pii(L[i], R[i]));// }F (i, 1, n) {if (b[i] != b[i - 1]) {h[++ovo] = {L[i], R[i], b[i]};}}sort(h + 1, h + 1 + ovo, [&](const auto& lhs, const auto& rhs) {return lhs.r > rhs.r;});// fenwick bit(n);sort(q + 1, q + 1 + OoO, [&](const auto& lhs, const auto& rhs) {return lhs.r > rhs.r;});F (i, 1, OoO, j = 1) {while (j <= ovo and h[j].r >= q[i].r) {fenwick::add(h[j].l, h[j].dep); j++;}ckmax(ans[ q[i].id ], fenwick::query( q[i].r - q[i].k + 1 ));}sort(h + 1, h + 1 + ovo, [&](const auto& lhs, const auto& rhs) {return lhs.r - lhs.l > rhs.r - rhs.l;});sort(q + 1, q + 1 + OoO, [&](const auto& lhs, const auto& rhs) {return lhs.k > rhs.k;}); F (i, 1, OoO, j = 1) {while (j <= ovo and h[j].r - h[j].l + 1 >= q[i].k) {seg::update(1, 1, n, h[j].r, h[j].dep); j++;}ckmax(ans[ q[i].id ], seg::query(1, 1, n, q[i].l + q[i].k - 1, q[i].r));}F (i, 1, Q) {cout << ans[i] << "\n";}
}