当前位置:   article > 正文

可持久化线段树_可持久化权值线段树

可持久化权值线段树

可持久化线段树就是可以查询和修改历史版本的线段树

具体实现:

  1. 我们采用动态开点的方式来建立线段树(当然也可以离散化)
  2. build和query操作与普通动态开点线段树如出一辙
  3. 对于修改操作,我们新增修改后的结点及其父结点,未修改过的结点直接copy过来
  4. 对于一次修改,就是一次新增祖宗的过程,也是一次添加新版本的过程

各变量含义:

t[i] - i所表示区间的权值 

lson[p] - p结点左儿子所在位置

rson[p] - p结点右儿子所在位置

v[i] - 版本i起始结点所在位置

cnt - 下一个新增结点所在位置

a - 原数组

change函数中:p - 历史版本,q - 新版本

题目链接:可持久化线段树 1 

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int t[20000020], lson[20000020], rson[20000020], v[10000020], cnt = 2, a[10000005];
  4. void build(int p, int l, int r) {
  5. if (l == r) {
  6. t[p] = a[l];
  7. } else {
  8. lson[p] = cnt++;
  9. rson[p] = cnt++;
  10. int m = l + r >> 1;
  11. build(lson[p], l, m);
  12. build(rson[p], m + 1, r);
  13. t[p] = t[lson[p]] + t[rson[p]];
  14. }
  15. }
  16. void change(int p, int q, int l, int r, int x, int d) {
  17. if (r < x || l > r) {
  18. lson[q] = lson[p];
  19. rson[q] = rson[p];
  20. } else if (l == r && l == x) {
  21. t[q] = d;
  22. } else {
  23. lson[q] = lson[p];
  24. rson[q] = rson[p];
  25. int m = l + r >> 1;
  26. if (x <= m) {
  27. lson[q] = cnt++;
  28. change(lson[p], lson[q], l, m, x, d);
  29. } else {
  30. rson[q] = cnt++;
  31. change(rson[p], rson[q], m + 1, r, x, d);
  32. }
  33. t[q] = t[lson[q]] + t[rson[q]];
  34. }
  35. }
  36. int query(int p, int l, int r, int ql, int qr) {
  37. if (l > qr || r < ql) {
  38. return 0;
  39. } else if (ql <= l && r <= qr) {
  40. return t[p];
  41. } else {
  42. int m = l + r >> 1;
  43. return query(lson[p], l, m, ql, qr) + query(rson[p], m + 1, r, ql, qr);
  44. }
  45. }
  46. int main() {
  47. ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
  48. int n, m;
  49. cin >> n >> m;
  50. for (int i = 1; i <= n; i++) {
  51. cin >> a[i];
  52. }
  53. build(1, 1, n);
  54. v[0] = 1;
  55. for (int i = 1; i <= m; i++) {
  56. int vi, op, x, d;
  57. cin >> vi >> op;
  58. if (op == 1) {
  59. cin >> x >> d;
  60. v[i] = cnt++;
  61. change(v[vi], v[i], 1, n, x, d);
  62. } else {
  63. cin >> x;
  64. v[i] = v[vi];
  65. cout << query(v[vi], 1, n, x, x) << endl;
  66. }
  67. }
  68. return 0;
  69. }

主席树

背景:给定 n 个整数构成的序列 a,将对于指定的闭区间 [l, r] 查询其区间内的第 k 小值。

1\leq n,m\leq 2\times 10^{5}

主席树全称为可持久化权值线段树,简单来说,第i个版本的线段树存的是序列[1...i]构成的权值线段树,而用权值线段树很容易求出区间第k小,容易发现权值线段树是有可减性的,想要得到区间[l, r]的权值线段树,可以用区间[1, r] - 区间[1, l - 1],从而求出第k小。具体过程可以去【Notes】【主席树】hdu2665 Kth number_juruo? juruo!-CSDN博客

  1. // from : https://oi-wiki.org/ds/persistent-seg/
  2. #include <algorithm>
  3. #include <cstdio>
  4. #include <cstring>
  5. #define int long long
  6. using namespace std;
  7. const int maxn = 2e5; // 数据范围
  8. int tot, n, m;
  9. int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10],
  10. rs[(maxn << 5) + 10];
  11. int a[maxn + 10], ind[maxn + 10], len;
  12. inline int getid(const int &val) { // 离散化
  13. return lower_bound(ind + 1, ind + len + 1, val) - ind;
  14. }
  15. int build(int l, int r) { // 建树
  16. int root = ++tot;
  17. if (l == r) return root;
  18. int mid = l + r >> 1;
  19. ls[root] = build(l, mid);
  20. rs[root] = build(mid + 1, r);
  21. return root; // 返回该子树的根节点
  22. }
  23. int update(int k, int l, int r, int root) { // 插入操作
  24. int dir = ++tot;
  25. ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1;
  26. if (l == r) return dir;
  27. int mid = l + r >> 1;
  28. if (k <= mid)
  29. ls[dir] = update(k, l, mid, ls[dir]);
  30. else
  31. rs[dir] = update(k, mid + 1, r, rs[dir]);
  32. return dir;
  33. }
  34. int query(int u, int v, int l, int r, int k) { // 查询操作
  35. int mid = l + r >> 1,
  36. x = sum[ls[v]] - sum[ls[u]]; // 通过区间减法得到左儿子中所存储的数值个数
  37. if (l == r) return l;
  38. if (k <= x) // 若 k 小于等于 x ,则说明第 k 小的数字存储在在左儿子中
  39. return query(ls[u], ls[v], l, mid, k);
  40. else // 否则说明在右儿子中
  41. return query(rs[u], rs[v], mid + 1, r, k - x);
  42. }
  43. inline void init() {
  44. scanf("%d%d", &n, &m);
  45. for (int i = 1; i <= n; ++i) scanf("%d", a + i);
  46. memcpy(ind, a, sizeof ind);
  47. sort(ind + 1, ind + n + 1);
  48. len = unique(ind + 1, ind + n + 1) - ind - 1;
  49. rt[0] = build(1, len);
  50. for (int i = 1; i <= n; ++i) rt[i] = update(getid(a[i]), 1, len, rt[i - 1]);
  51. }
  52. int l, r, k;
  53. inline void work() {
  54. while (m--) {
  55. scanf("%d%d%d", &l, &r, &k);
  56. printf("%d\n", ind[query(rt[l - 1], rt[r], 1, len, k)]); // 回答询问
  57. }
  58. }
  59. signed main() {
  60. init();
  61. work();
  62. return 0;
  63. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/180141
推荐阅读
相关标签
  

闽ICP备14008679号