(以下是luogu题面)

题目描述

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

输入输出格式

输入格式:

第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v,k),表示一组询问。

输出格式:

M行,表示每个询问的答案。

说明

HINT:

N,M<=100000

思路:

  一看到静态第K小,你就要想到主席树;一看到树,你就要树链剖分。

  的确用树剖把树搞成区间,在上面架主席树是比较直接的想法。问题是树剖把链所划分成的区间的数目是不确定的,因此树剖能维护的一般是能够对每条链独立求出,再用结合律结合得出答案的信息。但是主席树需要用确定多的几棵前缀权值树维护一个虚拟的树,查询函数每次传入的参数最好是一样多的。我猜树剖可以写,但是实在太麻烦,而且还多一个log。

  我们需要维护的其实只是两点之间简单路径的信息,联想到用树上差分实现树链修改的过程,我们可以用主席树维护一个类似于前缀的东西:定义root[i]表示从原树根节点到点i的路径上的权值线段树的根。每棵新树基于的原始版本是它父亲u的那棵树。这个思想与区间前缀和的关系就好比字符串之于Trie树(可能这么比喻也不恰当)。按dfs序建立这样的主席树之后,我们查询的树上路径可以这样求出:

  设S[l, r]维护从l到r路径的值域信息的线段树,则

    S[u, v] = S[root, u] + S[root, v] - S[root, lca(u, v)] - S[root, father(lca(u, v))]

  可以看到这个形式与树上节点信息差分很像。那么我们还需要维护LCA的查询。(终于可以用树剖辣!@w@……不,你不想)

  (倍增大法好

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <cctype>
  6. #define BUG puts("$$$")
  7. #define LG 17
  8. #define maxn 100010
  9. template <class T>
  10. void read(T &x) {
  11. x = 0;
  12. char ch = getchar();
  13. while (!isdigit(ch))
  14. ch = getchar();
  15. while (isdigit(ch)) {
  16. x = x * 10 + (ch ^ 48);
  17. ch = getchar();
  18. }
  19. }
  20. using namespace std;
  21. struct E {
  22. int to, nxt;
  23. } edge[maxn << 1];
  24. int head[maxn], top;
  25. int n, m;
  26. int a[maxn], N, st[maxn];
  27. inline void insert(int u, int v) {
  28. edge[++top] = (E) {v, head[u]};
  29. head[u] = top;
  30. }
  31. namespace LCA {
  32. int f[LG + 2][maxn], d[maxn];
  33. void dfs(int u, int pre) {
  34. d[u] = d[pre] + 1;
  35. f[0][u] = pre;
  36. for (int i = head[u]; i; i = edge[i].nxt) {
  37. int v = edge[i].to;
  38. if (v != pre)
  39. dfs(v, u);
  40. }
  41. }
  42. void init1() {
  43. dfs(1, 0);
  44. for (int k = 1; k <= LG; ++k)
  45. for (int i = 1; i <= n; ++i)
  46. f[k][i] = f[k-1][f[k-1][i]];
  47. }
  48. void swim(int &x, int d) {
  49. for (int i = 0; d; d >>= 1, ++i)
  50. if (d & 1)
  51. x = f[i][x];
  52. }
  53. int find(int u, int v) {
  54. if (d[u] > d[v]) swap(u, v);
  55. swim(v, d[v] - d[u]);
  56. if (u == v)
  57. return u;
  58. for (int k = LG; k >= 0; --k)
  59. if (f[k][u] != f[k][v])
  60. u = f[k][u], v = f[k][v];
  61. return f[0][u];
  62. }
  63. } using namespace LCA;
  64. namespace President_tree {
  65. #define lc(i) seg[i].lc
  66. #define rc(i) seg[i].rc
  67. #define mid ((l + r) >> 1)
  68. int root[maxn], tot;
  69. struct node {
  70. int cnt, lc, rc;
  71. } seg[maxn * 30];
  72. inline void update(int nd) {
  73. seg[nd].cnt = seg[lc(nd)].cnt + seg[rc(nd)].cnt;
  74. }
  75. int build(int l, int r) {
  76. int nd = ++tot;
  77. seg[nd].cnt = 0;
  78. if (l == r)
  79. return nd;
  80. lc(nd) = build(l, mid);
  81. rc(nd) = build(mid + 1, r);
  82. return nd;
  83. }
  84. int modify(int pre, int l, int r, int x) {
  85. int nd = ++tot;
  86. seg[nd] = seg[pre];
  87. if (l == r) {
  88. ++seg[nd].cnt;
  89. return nd;
  90. }
  91. if (x <= mid)
  92. lc(nd) = modify(lc(pre), l, mid, x);
  93. else rc(nd) = modify(rc(pre), mid + 1, r, x);
  94. update(nd);
  95. return nd;
  96. }
  97. void init2(int u, int pre) {
  98. root[u] = modify(root[pre], 1, N, a[u]);
  99. for (int i = head[u]; i; i = edge[i].nxt) {
  100. int v = edge[i].to;
  101. if (v != pre)
  102. init2(v, u);
  103. }
  104. }
  105. int query(int u, int v, int lca, int flca, int l, int r, int k) {
  106. if (l == r) {
  107. return st[l];
  108. }
  109. int lsum = seg[lc(u)].cnt + seg[lc(v)].cnt - seg[lc(lca)].cnt - seg[lc(flca)].cnt;
  110. if (k <= lsum)
  111. return query(lc(u), lc(v), lc(lca), lc(flca), l, mid, k);
  112. return query(rc(u), rc(v), rc(lca), rc(flca), mid + 1, r, k - lsum);
  113. }
  114. } using namespace President_tree;
  115. int contra(int* a) {
  116. for (int i = 1; i <= n; ++i)
  117. st[i] = a[i];
  118. sort(st + 1, st + 1 + n);
  119. int len = unique(st + 1, st + 1 + n) - st - 1;
  120. for (int i = 1; i <= n; ++i)
  121. a[i] = lower_bound(st + 1, st + len + 1, a[i]) - st;
  122. return len;
  123. }
  124. int main() {
  125. read(n), read(m);
  126. int u, v;
  127. for (int i = 1; i <= n; ++i)
  128. read(a[i]);
  129. N = contra(a);
  130. for (int i = 1; i < n; ++i) {
  131. read(u), read(v);
  132. insert(u, v), insert(v, u);
  133. }
  134. init1();
  135. init2(1, 0);
  136. int k, ans = 0;
  137. for (int i = 1; i <= m; ++i) {
  138. read(u), read(v), read(k);
  139. u = ans xor u;
  140. int lca = find(u, v);
  141. ans = query(root[u], root[v], root[lca], root[f[0][lca]], 1, N, k);
  142. printf("%d\n", ans);
  143. }
  144. return 0;
  145. }

【bzoj2588/P2633】count on a tree —— LCA + 主席树的更多相关文章

  1. 【BZOJ2588】Count On a Tree(主席树)

    [BZOJ2588]Count On a Tree(主席树) 题面 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第 ...

  2. [BZOJ2588]Count on a tree(LCA+主席树)

    题面 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问 ...

  3. 洛谷P2633 Count on a tree(主席树,倍增LCA)

    洛谷题目传送门 题目大意 就是给你一棵树,每个点都有点权,每次任意询问两点间路径上点权第k小的值(强制在线). 思路分析 第k小......又是主席树了.但这次变成树了,无法直接维护前缀和. 又是树上 ...

  4. 洛谷P2633 Count on a tree(主席树,倍增LCA,树上差分)

    洛谷题目传送门 题目大意 就是给你一棵树,每个点都有点权,每次任意询问两点间路径上点权第k小的值(强制在线). 思路分析 第k小......又是主席树了.但这次变成树了,无法直接维护前缀和. 又是树上 ...

  5. BZOJ2588 SPOJ10628 Count on a tree 【主席树】

    BZOJ2588 Count on a tree 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中l ...

  6. BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )

    Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...

  7. Count on a tree 树上主席树

    Count on a tree 树上主席树 给\(n\)个树,每个点有点权,每次询问\(u,v\)路径上第\(k\)小点权,强制在线 求解区间静态第\(k\)小即用主席树. 树上主席树类似于区间上主席 ...

  8. [Bzoj2588]Count on a tree(主席树+LCA)

    Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...

  9. 【洛谷 P2633】 Count on a tree(主席树,树上差分)

    题目链接 思维难度0 实现难度7 建出主席树后用两点的状态减去lca和lca父亲的状态,然后在新树上跑第\(k\)小 #include <cstdio> #include <cstr ...

随机推荐

  1. F. Make It Connected 解析(思維、MST)

    Codeforce 1095 F. Make It Connected 解析(思維.MST) 今天我們來看看CF1095F 題目連結 題目 給你\(n\)個點,每個點\(u\)還有一個值\(a[u]\ ...

  2. DevOps 视角的前后端分离与实战

    本文作者:CODING - 廖红坤 前言 随着微前端.微服务等技术理念和架构的蓬勃发展,我们已经没必要去讨论为什么要前后端分离这种话题,前后端分离已成为互联网项目开发的标准模式.前后端在各自的领域发展 ...

  3. [Luogu P4124] [CQOI2016]手机号码 (数位DP)

    题面 传送门:洛咕 Solution 感谢神仙@lizbaka的教学 这题是数位DP的非常非常模板的题目,只是状态有点多 . 这题我使用记忆化搜索实现的 中国有句古话说的好,有多少个要求就设多少个状态 ...

  4. 令人惊叹的百度Echarts,大数据分析的必备工具

    学习目录 1.可视化面板介绍     1.1技术要点     1.2案例适配方案     1.3页面主体布局2.Echarts(重点)     2.1echarts介绍     2.2echarts体 ...

  5. .netcore3.1使用log4net/nlog记录日志

    .netcore3.1使用log4net/nlog记录日志 .netcore3.1与2.x之间很是有不少差异的.本来想通过ctrl+c,ctrl+v将在2.2中实现的简单日志记录搬到.netcore3 ...

  6. 聊聊Go代码覆盖率技术与最佳实践

    "聊点干货" 覆盖率技术基础 截止到Go1.15.2以前,关于覆盖率技术底层实现,以下知识点您应该知道: go语言采用的是插桩源码的形式,而不是待二进制执行时再去设置breakpo ...

  7. C++ 基础 3:类和对象

    1 类和对象 1.1 类定义 类定义是以关键字 class 开头,后跟类的名称.类的主体是包含在一对花括号中.类定义后必须跟着一个分号或一个声明列表.例如,我们使用关键字 class 定义 Box 数 ...

  8. WSL-Ubuntu18.04 磁盘迁移 与 ns3-gym 安装

    WSL 安装 win10 版本应大于或等于 1903 win10 设置页面 输入 控制面板 并点击进入 找到 程序和功能 并打开 找到 启动或关闭 Windows 功能 并打开 向下拉 勾选 适用于L ...

  9. tp6.0.x 反序列化漏洞

    tp6 反序列化漏洞复现 环境 tp6.0 apache php7.3 漏洞分析 反序列化漏洞需要存在 unserialize() 作为触发条件,修改入口文件 app/controller/Index ...

  10. tcpack--3快速确认模式

    接收到数据报后,会调用tcp_event_data_recv(),不管是在慢速路径的tcp_data_queue中调用还是 在快速路径中处理接收数据后直接调用,注意(如果len <= tcp_h ...