当前位置: 首页 > news >正文

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

http://www.jsqmd.com/news/772675/

相关文章:

  • STM32F103串口调试避坑大全:从CubeMX配置到printf重定向,解决你99%的常见问题
  • Taotoken 透明计费如何让个人开发者清晰规划项目预算
  • 工業級 AI 平台及具身智能應用
  • 基于AI的本地网络流量监控工具wirewatch:从原理到实战部署
  • 通达信ChanlunX缠论插件:3步实现专业缠论分析的终极免费工具
  • 原神玩家必备:Snap.Hutao工具箱终极效率提升指南
  • 不止是ethtool:在Ubuntu 22.04上实现网络唤醒的三种方法对比
  • 【奇点内部速递】:AISMM v2.3正式版已冻结开发,但ESG动态权重算法仍对首批200家认证企业开放灰度接入(限时72小时)
  • 从社交关系到分子结构:图解GCN(图卷积网络)到底在学什么?
  • 利用Taotoken多模型聚合能力优化AI应用选型策略
  • 终极指南:如何用M9A自动化助手轻松玩转《重返未来:1999》
  • Unity新手避坑指南:手把手教你用NuGet搞定LitJSON安装(附.NET版本查看)
  • 别再死磕SIFT了!2024年用OpenCV+Python搞定SFM三维重建(附完整代码)
  • 单光束拉曼跃迁在量子计算中的原理与应用
  • 多端开发的协同之痛,行业正在怎么解? - 领先技术探路人
  • 毕业设计:基于Springboot+Vue的甜品销售系统(源码)
  • 从磁铁选型到角度校准:手把手教你用Arduino和AS5600打造高精度旋转传感器(附磁铁间距实测数据)
  • 太仓常熟张家港吴江发电机出租5月最新攻略:2026年全方位租赁发电机实用指南发布 - 奋斗者888
  • ICode竞赛Python一级通关秘籍:手把手教你用变量和循环搞定基础训练2
  • Windows 11/10下Vivado安装避坑指南:如何正确设置以杜绝综合死机
  • S32K118实战:用NXP SDK的FLEXCAN驱动实现按键控制LED(附完整代码)
  • 商场电梯贴膜
  • 基于Agentic RAG与PGVector的YouTube视频智能问答系统构建指南
  • 我的世界java手机版下载(FCL启动器)最新版下载分享
  • 如何永久收藏TIDAL无损音乐?开源工具tidal-dl-ng让你真正拥有高品质音乐
  • 从实验室混乱到井然有序:一个真实的学生项目如何用Vue+SpringBoot解决元器件管理难题(含完整数据库设计)
  • 创业团队如何利用Taotoken模型广场快速进行AI能力选型与验证
  • Kubernetes探针之livenessProbe探针
  • 自托管AI网关HydeClaw:整合28种AI模型与多平台接入的智能体编排平台
  • AISMM模型实战手册:从技术债评估、场景优先级排序到资源动态分配的完整闭环