题意

给定一棵 \(n\) 个点的带点权树,以 \(1\) 为根, \(m\) 次询问,每次询问给出两个值 \(p, k\) ,求以下值:

\(p\) 的子树中距离 \(p \le k\) 的所有点权最小值,询问强制在线。

\(n \le 10^5 , m \le 10^6, TL = 6s\)

题解

如果不强制在线,直接线段树合并就做完了。

强制在线,不难想到用一些可持久化的结构来维护这些东西。

其实可以类似线段树合并那样考虑,也就是说每次合并的时候我们依然使用儿子的信息。

只要在合并 \(x, y\) 共有部分的时候建出新节点,然后权值是 \(x, y\) 权值的较小值,其他的部分直接连向那些单独有的子树信息即可。

其实实现是这样的:

	int Merge(int x, int y) {
if (!x || !y) return x | y;
int o = Newnode();
ls[o] = Merge(ls[x], ls[y]);
rs[o] = Merge(rs[x], rs[y]);
minv[o] = min(minv[x], minv[y]);
return o;
}

这样的话,既可以保留子树信息,又可以得到这个节点新的信息。

最后空间复杂度就是 \(O(n \log n)\) ,时间复杂度是 \(O((n + m) \log n)\) 的。

总结

强制在线问题,用可持久化数据结构去解决就行了,也就是把平常的数据结构记历史版本,并尽量用之前的信息。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define pb push_back using namespace std; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("F.in", "r", stdin);
freopen ("F.out", "w", stdout);
#endif
} const int N = 1e5 + 1e3, inf = 0x3f3f3f3f; #define lson ls[o], l, mid
#define rson rs[o], mid + 1, r template<int Maxn>
struct Chair_Man_Tree { int ls[Maxn], rs[Maxn], minv[Maxn], Size; Chair_Man_Tree() { minv[0] = inf; } inline int Newnode() {
int o = ++ Size; minv[o] = inf; return o;
} void Update(int &o, int l, int r, int up, int uv) {
if (!o) o = Newnode();
if (l == r) { chkmin(minv[o], uv); return ; }
int mid = (l + r) >> 1;
up <= mid ? Update(lson, up, uv) : Update(rson, up, uv);
minv[o] = min(minv[ls[o]], minv[rs[o]]);
} int Query(int o, int l, int r, int ql, int qr) {
if (!o) return inf;
if (ql <= l && r <= qr) return minv[o];
int mid = (l + r) >> 1;
if (qr <= mid) return Query(lson, ql, qr);
if (ql > mid) return Query(rson, ql, qr);
return min(Query(lson, ql, qr), Query(rson, ql, qr));
} int Merge(int x, int y) {
if (!x || !y) return x | y;
int o = Newnode();
ls[o] = Merge(ls[x], ls[y]);
rs[o] = Merge(rs[x], rs[y]);
minv[o] = min(minv[x], minv[y]);
return o;
} }; vector<int> G[N]; int val[N], rt[N], dep[N]; int n, S; Chair_Man_Tree<N * 80> T; void Dfs(int u, int fa = 0) {
dep[u] = dep[fa] + 1;
for (int v : G[u]) if (v != fa)
Dfs(v, u), rt[u] = T.Merge(rt[u], rt[v]);
T.Update(rt[u], 1, n, dep[u], val[u]);
} int main () { File(); n = read(); S = read(); For (i, 1, n)
val[i] = read(); For (i, 1, n - 1) {
int u = read(), v = read();
G[u].pb(v); G[v].pb(u);
} Dfs(S); int q = read(), ans = 0;
For (i, 1, q) {
int x = (read() + ans) % n + 1, k = (read() + ans) % n;
printf ("%d\n", ans = T.Query(rt[x], 1, n, dep[x], min(dep[x] + k, n)));
} return 0; }

最新文章

  1. JavaScript面试时候的坑洼沟洄——逗号、冒号与括号
  2. stm32控制电机
  3. ELb表达式
  4. C#基于Socket的简单聊天室实践
  5. 避免产生僵尸进程的N种方法(zombie process)
  6. iOS中Block使用探索
  7. Note_Master-Detail Application(iOS template)_05_ YJYMasterViewController.m
  8. Android RecyclerView添加Header头部
  9. JS与C#编码解码
  10. android moveTaskToback 应用退到后台,类似最小化
  11. Git 版本控制工具(学习笔记)
  12. Eclipse配置Git发布项目到Github
  13. js转base64(数字)
  14. springBoot生成日志文件
  15. spring boot 整合 百度ueditor富文本
  16. zookeepeer使用java api
  17. 关于APP,原生和H5开发技术的争论 APP开发技术选型判断依据
  18. Markdown 标题
  19. 冰与火之歌居然是在 DOS 系统上写出来的
  20. 【CF840C】On the Bench DP

热门文章

  1. Makes And The Product CodeForces - 817B (思维+构造)
  2. Elasticsearch的DSL之比较重要的几个查询语句
  3. freemarker根据模板生成word文件实现导出功能
  4. Python_面向对象_单例模式
  5. Python_函数的有用信息、带参数的装饰器、多个装饰器装饰一个函数
  6. Linux下破解pycharm
  7. 项目管理、软件、禅道 VS JIRA
  8. 网站数据分析&amp;初始来源
  9. winform启动界面+登录窗口
  10. Bootstrap知识记录:表单和图片