F. Subtree Minimum Query

F. Subtree Minimum Query

You are given a rooted tree consisting of $n$ vertices. Each vertex has a number written on it; number $a_i$ is written on vertex $i$.

Let's denote $d(i, j)$ as the distance between vertices $i$ and $j$ in the tree (that is, the number of edges in the shortest path from $i$ to $j$). Also let's denote the $k$-blocked subtree of vertex $x$ as the set of vertices $y$ such that both these conditions are met:

  • $x$ is an ancestor of $y$ (every vertex is an ancestor of itself);
  • $d(x, y) \leq k$.

You are given m queries to the tree. $i$-th query is represented by two numbers $x_i$ and $k_i$, and the answer to this query is the minimum value of $a_j$ among such vertices $j$ such that $j$ belongs to $k_i$-blocked subtree of $x_i$.

Write a program that would process these queries quickly!

Note that the queries are given in a modified way.

Input

The first line contains two integers $n$ and $r$ $(1 \leq r \leq n \leq 100000)$ — the number of vertices in the tree and the index of the root, respectively.

The second line contains $n$ integers $a_1, a_2, \ldots, a_n$ $(1 \leq a_i \leq 10^9)$ — the numbers written on the vertices.

Then $n - 1$ lines follow, each containing two integers $x$ and $y$ $(1 \leq x, y \leq n)$ and representing an edge between vertices $x$ and $y$. It is guaranteed that these edges form a tree.

Next line contains one integer $m$ $(1 \leq m \leq 10^6)$ — the number of queries to process.

Then $m$ lines follow, $i$-th line containing two numbers $p_i$ and $q_i$, which can be used to restore $i$-th query $(1 \leq p_i, q_i \leq n)$.

$i$-th query can be restored as follows:

Let $last$ be the answer for previous query (or $0$ if $i = 1$). Then $x_i = ((p_i + last) \bmod n) + 1$, and $k_i = (q_i + last) \bmod n$.

Output

Print $m$ integers. $i$-th of them has to be equal to the answer to $i$-th query.

Example

Input

5 2
1 3 2 3 5
2 3
5 1
3 4
4 1
2
1 2
2 3

Output

2
5

 

解题思路

  本题的每次询问相当于在以 $x$ 为根的子树中,求与 $x$ 距离不超过 $k$ 的节点权值的最小值。通过引入 DFS 序,我们可以将树上的子树问题转化为序列上的区间问题。设节点 $x$ 对应的 DFS 序区间为 $[l_x, r_x]$,则合法节点 $y$ 的 DFS 序需满足 $l_x \leq l_y \leq r_x$。定义 $d_x$ 为节点 $x$ 到根节点的距离,由于 $y$ 是 $x$ 的后代,两者间的距离即为深度差 $d_y - d_x$,因此距离不超过 $k$ 等价于 $d_y - d_x \leq k$,即 $d_y \leq d_x + k$。综上,所求点集可表示为满足 $l_x \leq l_y \leq r_x$ 且 $d_y \leq d_x + k$ 的节点 $y$ 的集合。

  虽然题目要求强制在线处理查询,但我们可以先考虑离线做法以寻求启发。在给定 $x$ 和 $k$ 时,约束 $d_y \leq d_x + k$ 意味着合法节点 $y$ 的深度不超过某一固定上界。因此,我们只需在所有深度不超过 $d_x + k$ 且 DFS 序落在 $[l_x, r_x]$ 内的节点中寻找最小点权。如果对每个询问单独处理显然会超时,但若允许离线,我们可将询问按 $d_x + k$ 升序排序,并按此顺序处理。在处理当前询问前,将深度不超过 $d_x + k$ 的所有节点插入线段树(插入位置为节点的 DFS 序,值为其权值)。此时线段树中恰好包含了所有深度不超过 $d_x + k$ 的节点,直接查询区间 $[l_x, r_x]$ 的最小值即为该询问的答案。

  对于在线查询而言,我们无法为每个询问重新构建线段树,但如果能快速获取维护了深度不超过 $d_x + k$ 的所有节点的线段树,就能快速查询答案。一个自然的想法是直接保存每个深度对应的线段树,这便引出了可持久化线段树的应用。由于深度为 $d$ 的线段树是在深度为 $d-1$ 的线段树基础上扩展而来,我们只需在上一深度的历史版本上依次插入当前深度的节点,即可构建出当前深度的版本。只需在处理询问前通过预处理构建可持久化线段树即可,时间和空间复杂度均为 $O(n \log n)$。

  这种利用可持久化线段树扩展历史版本的思路,与 G - Copy Query 相似。

  AC 代码如下,时间复杂度为 $O((n+q) \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 5, M = N * 2;

int a[N];
int h[N], e[M], ne[M], idx;
int tin[N], tout[N], d[N];
int p[N];
struct Node {
    int l, r, mn;
}tr[N * 35];
int root[N];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void dfs(int u, int p) {
    tin[u] = ++idx;
    d[u] = d[p] + 1;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        dfs(v, u);
    }
    tout[u] = idx;
}

int modify(int u, int l, int r, int x, int c) {
    int v = ++idx;
    tr[v] = tr[u];
    if (l == r) {
        tr[v].mn = c;
    }
    else {
        int mid = l + r >> 1;
        if (x <= mid) tr[v].l = modify(tr[u].l, l, mid, x, c);
        else tr[v].r = modify(tr[u].r, mid + 1, r, x, c);
        tr[v].mn = min(tr[tr[v].l].mn, tr[tr[v].r].mn);
    }
    return v;
}

int query(int u, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr) return tr[u].mn;
    int mid = l + r >> 1;
    if (qr <= mid) return query(tr[u].l, l, mid, ql, qr);
    if (ql >= mid + 1) return query(tr[u].r, mid + 1, r, ql, qr);
    return min(query(tr[u].l, l, mid, ql, qr), query(tr[u].r, mid + 1, r, ql, qr));
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    memset(h, -1, sizeof(h));
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
    }
    idx = 0;
    dfs(m, 0);
    iota(p + 1, p + 1 + n, 1);
    sort(p + 1, p + n + 1, [&](int i, int j) {
        return d[i] < d[j];
    });
    idx = 0;
    tr[0].mn = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++) {
        root[d[p[i]]] = modify(root[d[p[i - 1]]], 1, n, tin[p[i]], a[p[i]]);
    }
    int ret = 0;
    cin >> m;
    while (m--) {
        int x, k;
        cin >> x >> k;
        x = (x + ret) % n + 1, k = (k + ret) % n;
        ret = query(root[min(d[p[n]], d[x] + k)], 1, n, tin[x], tout[x]);
        cout << ret << '\n';
    }
    
    return 0;
}

  对本题进一步扩展。假设每次查询的是在以 $x$ 为根的子树中,求与 $x$ 距离至少为 $lb$ 且不超过 $ub$ 的节点权值最小值,约束条件将变为 $l_x \leq l_y \leq r_x$ 且 $d_x + lb \leq d_y \leq d_x + ub$。在强制在线的情况下,上述的可持久化线段树做法不再适用,可能需要用树套树来维护深度与 DFS 序的两维约束,由于博主不了解这里暂不展开讨论。若允许离线处理,则可使用 dsu on tree 来解决。

  具体而言,先将询问按节点 $x$ 分类,分发到每个对应的节点。在 dfs 遍历树的过程中,对于当前节点 $x$,先求解其所有轻儿子的答案,再求解重儿子的答案。此时利用线段树维护重儿子对应子树中各节点在其深度位置上的权值,接着将轻儿子对应子树的节点也插入该线段树。最后,对于当前节点 $x$ 的所有询问,直接在维护好的线段树上查询深度区间 $[d_x + lb, d_x + ub]$ 的最小值即可。该离线做法的时间复杂度为 $O\left(n \log^2{n} + q \left(\log{q} + \log{n}\right)\right)$。

 

参考资料

  Educational Codeforces Round 33 Editorial - Codeforces:https://codeforces.com/blog/entry/55989

  灵茶の试炼:https://docs.qq.com/sheet/DWGFoRGVZRmxNaXFz

posted @ 2026-05-07 21:54  onlyblues  阅读(17)  评论(0)    收藏  举报
Web Analytics