(以下是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. 开源项目bootdo的实战开发笔记

    开源项目bootdo 源码地址:https://github.com/lcg0124/bootdo 技术选型 1.后端 核心框架:Spring Boot 安全框架:Apache Shiro 模板引擎: ...

  2. Java 复制到剪贴板

    public void copy(String str) { StringSelection stsel = new StringSelection(str); Toolkit.getDefaultT ...

  3. Kubernetes 使用 Ingress 实现灰度发布功能

    使用 Ingress 实现灰度发布 一.Canary 规则说明 Ingress-Nginx 是一个K8S ingress工具,支持配置 Ingress Annotations 来实现不同场景下的灰度发 ...

  4. Iperius Backup Full--小中企业简单自动备份的实用工具

    从事IT行业几个年头了,一直以来发现备份这个词是十分特殊的.无论是事业国有大企央企还是个人爱好者,小型工作室,中小企业. 对于备份都是明确知道十分重要,但在正在实施起来会因为投入,领导重视程度,实施管 ...

  5. flask-profiler的使用

    使用 profiler测量在你的Flask 应用程序中定义的端点:并通过web界面提供细粒度的报告. 它给出了这些问题的答案: 应用程序中的瓶颈在哪里? 应用程序中最慢的终结点? 哪些是最常被调用的终 ...

  6. 这么设置Intellij IDEA,据说效率还不错~

    显示工具条 (1)效果图 (2)设置方法 标注1:View–>Toolbar 标注2:View–>Tool Buttons 设置鼠标悬浮提示 (1)效果图 (2)设置方法File–> ...

  7. CF1413C Perform Easily 题解

    毒瘤C题,考场卡我1个小时 首先,这道题难点在哪里?它的最大值与最小值都是浮动的. 怎么办?把最小/最大值固定! 以把最小值固定为例,我们枚举每个音符,并枚举它使用哪条琴弦,将它此时的位置强制其作为最 ...

  8. visual c++6.0使用VA注意事项

    visual c++6.0使用VA时配置: (1)因为VA安装时会自动检索MSDEV.exe:如果V6安装在XP或者win7系统上,直接安装,添加addin即可: (2)但是如果安装在win8上,V6 ...

  9. System.Net邮件发送功能踩过的坑

    System.Net邮件发送功能踩过的坑 目录 System.Net邮件发送功能踩过的坑 1.EazyEmail邮件发送类库 2.邮件发送授权码与邮件密码 3.通过邮件密码来发送邮件 4.Wiresh ...

  10. 18、Celery

    Celery 1.什么是Clelery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统 专注于实时处理的异步任务队列 同时也支持任务调度 Celery架构 Celery的架构由三部分组 ...