浙江集训Day4,从早8:00懵B到晚21:00,只搞懂了可持久化线段树以及主席树的板子。今天只能记个大概,以后详细完善讲解。

  可持久化线段树指的是一种基于线段树的可回溯历史状态的数据结构。我们想要保存某一段序列的历史信息,可以朴素地开m倍的空间存储;而线段树是一种优秀的数据结构,它每次修改操作只把原先的数据修改log个节点,这就意味着我们只增加这些被修改的节点存储新信息就好了。传统的线段树用二叉树存储,但是因为涉及加点,可持久化线段树必须维护一个自增的tot动态成为新点的编号。对于每个节点,我们要维护两个指针指向它的左右子节点。对于每次修改,我们把新节点的不被修改的一半区间指向上个状态该区间的节点编号,等同于利用了上次的区间信息;另一半新开一个点继续递归下去即可。

  可持久化线段树一般不能维护区间修改,因为多个根共享子节点,我们没有办法对每个状态的树独立维护它的lazy_tag。

代码(动态开点&&区间最大值):

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <climits>
  6. #define maxn 1000100
  7. #define maxm 1000100
  8. #define LG 21
  9. #define BUG putchar('*')
  10. using namespace std;
  11. template <typename T>
  12. void read(T &x) {
  13. x = 0;
  14. int f = 1;
  15. char ch = getchar();
  16. while (!isdigit(ch)) {
  17. if (ch == '-')
  18. f = -1;
  19. ch = getchar();
  20. }
  21. while (isdigit(ch)) {
  22. x = x * 10 + (ch ^ 48);
  23. ch = getchar();
  24. }
  25. x *= f;
  26. return;
  27. }
  28. int n, m, a[maxn];
  29. namespace Segment_tree {
  30. int root[maxm], tot;
  31. struct node {
  32. int dat, lc, rc;
  33. } seg[maxn * 20];
  34. inline void update(int nd) {
  35. seg[nd].dat = max(seg[seg[nd].lc].dat, seg[seg[nd].rc].dat);
  36. }
  37. int build(int l, int r) {
  38. int nd = ++tot;
  39. if (l == r) {
  40. seg[nd].dat = a[l];
  41. return nd;
  42. }
  43. int mid = (l + r) >> 1;
  44. seg[nd].lc = build(l, mid), seg[nd].rc = build(mid + 1, r);
  45. update(nd);
  46. return nd;
  47. }
  48. int modify(int nd, int l, int r, int x, int val) {
  49. int now = ++tot;
  50. seg[now] = seg[nd];
  51. if (l == r) {
  52. seg[now].dat = val;
  53. return now;
  54. }
  55. int mid = (l + r) >> 1;
  56. if (x <= mid)
  57. seg[now].lc = modify(seg[nd].lc, l, mid, x, val);
  58. else seg[now].rc = modify(seg[nd].rc, mid + 1, r, x, val);
  59. update(now);
  60. return now;
  61. }
  62. int query(int nd, int l, int r, int ql, int qr) {
  63. if (ql <= l && qr >= r)
  64. return seg[nd].dat;
  65. if (ql > r || qr < l)
  66. return INT_MIN;
  67. int mid = (l + r) >> 1;
  68. return max(query(seg[nd].lc, l, mid, ql, qr), query(seg[nd].rc, mid + 1, r, ql, qr));
  69. }
  70. } using namespace Segment_tree;
  71. int main() {
  72. read(n), read(m);
  73. for (int i = 1; i <= n; ++i)
  74. read(a[i]);
  75. root[0] = build(1, n);
  76. int k, op, val, loc, l, r;
  77. for (int i = 1; i <= m; ++i) {
  78. read(k), read(op);
  79. if (op == 1) {
  80. read(loc), read(val);
  81. root[i] = modify(root[k], 1, n, loc, val);
  82. } else {
  83. read(l), read(r);
  84. printf("%d\n", query(root[k], 1, n, l, r));
  85. root[i] = root[k];
  86. }
  87. }
  88. return 0;
  89. }

  听说主席树是由fotile96主席发明的(总之是位神犇就对了)。主要用于维护数据区间的值域问题。

  给定一段区间,每次询问l,r区间内的第k小值。主席树是一个可持久化权值线段树:我们将右端点r抽象成时刻,建立(数据长度)个根,以root[t]表示区间[1, t]所维护的权值线段树的根。(模板题的权值需要离散化)如果查询[l, r],我们找出第l-1和第r棵线段树(将root[0]建成一棵空树),这两棵树对应节点信息做差,实质上就得到了一棵仅维护区间[l, r]的虚拟线段树的信息。我们动态跑出这棵树的同时二分它的左右子树:如果这棵虚拟树的左端点权值和sum大于等于k,说明第k大数在[l, mid]中,我们转而查询这段的第k大:如果sum小于k,说明所求数是区间[mid + 1, r]的第k-sum大数。实际上,在区间Kth数问题中,主席树以类似前缀和的思想维护了虚拟的维护[l, r]信息的权值线段树来查询对应区间信息的。

 代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <climits>
  6. #define maxn 200100
  7. #define maxm 200100
  8. #define LG 21
  9. #define BUG putchar('*')
  10. using namespace std;
  11. int n, m;
  12. template <typename T>
  13. void read(T &x) {
  14. x = 0;
  15. int f = 1;
  16. char ch = getchar();
  17. while (!isdigit(ch)) {
  18. if (ch == '-')
  19. f = -1;
  20. ch = getchar();
  21. }
  22. while (isdigit(ch)) {
  23. x = x * 10 + (ch ^ 48);
  24. ch = getchar();
  25. }
  26. x *= f;
  27. return;
  28. }
  29. namespace Contra {
  30. int st[maxn];
  31. int work(int *org, int *st) {
  32. for (int i = 1; i <= n; ++i)
  33. st[i] = org[i];
  34. sort(st + 1, st + 1 + n);
  35. int len = unique(st + 1, st + n + 1) - (st + 1);
  36. for (int i = 1; i <= n; ++i)
  37. org[i] = lower_bound(st + 1, st + len + 1, org[i]) - st;
  38. return len;
  39. }
  40. }
  41. int a[maxn], N;
  42. namespace President_tree {
  43. #define lc(i) seg[(i)].lc
  44. #define rc(i) seg[(i)].rc
  45. using namespace Contra;
  46. int tot, root[maxm];
  47. struct node {
  48. int cnt, lc, rc;
  49. } seg[maxm * 20];
  50. void update(int nd) {
  51. seg[nd].cnt = seg[lc(nd)].cnt + seg[rc(nd)].cnt;
  52. }
  53. int build(int l, int r) {//root[0]:empty_tree
  54. int nd = ++tot;
  55. if (l == r) return nd;
  56. int mid = (l + r) >> 1;
  57. seg[nd].lc = build(l, mid);
  58. seg[nd].rc = build(mid + 1, r);
  59. return nd;
  60. }
  61. int modify(int pre, int l, int r, int x) {
  62. int nd = ++tot;
  63. seg[nd] = seg[pre];
  64. if (l == r) {
  65. ++seg[nd].cnt;
  66. return nd;
  67. }
  68. int mid = (l + r) >> 1;
  69. if (x <= mid)
  70. lc(nd) = modify(lc(pre), l, mid, x);
  71. else
  72. rc(nd) = modify(rc(pre), mid + 1, r, x);
  73. update(nd);
  74. return nd;
  75. }
  76. int query(int ql, int qr, int l, int r, int k) {
  77. if (l == r) {
  78. return st[l];
  79. }
  80. int mid = (l + r) >> 1, sum = seg[lc(qr)].cnt - seg[lc(ql)].cnt;
  81. if (k <= sum)
  82. return query(lc(ql), lc(qr), l, mid, k);
  83. return query(rc(ql), rc(qr), mid + 1, r, k - sum);
  84. }
  85. void init() {
  86. N = work(a, st);
  87. root[0] = build(1, N);
  88. for (int i = 1; i <= n; ++i) {
  89. root[i] = modify(root[i - 1], 1, N, rk[i]);
  90. }
  91. }
  92. } using namespace President_tree;
  93. int main() {
  94. read(n), read(m);
  95. for (int i = 1; i <= n; ++i)
  96. read(a[i]);
  97. init();
  98. int l, r, k;
  99. for (int i = 1; i <= m; ++i) {
  100. read(l), read(r), read(k);
  101. printf("%d\n", query(root[l - 1], root[r], 1, N, k));
  102. }
  103. return 0;
  104. }

  

  

【数据结构模版】可持久化线段树 && 主席树的更多相关文章

  1. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  2. [学习笔记] 可持久化线段树&主席树

    众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...

  3. 权值线段树&&可持久化线段树&&主席树

    权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...

  4. 洛谷P3834 可持久化线段树(主席树)模板

    题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...

  5. bzoj 4408: [Fjoi 2016]神秘数 数学 可持久化线段树 主席树

    https://www.lydsy.com/JudgeOnline/problem.php?id=4299 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1 ...

  6. 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)

    J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...

  7. [POJ2104] K – th Number (可持久化线段树 主席树)

    题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

  8. 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))

    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...

  9. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

随机推荐

  1. D. Palindromic characteristics 解析(DP)

    Codeforce 835 D. Palindromic characteristics 解析(DP) 今天我們來看看CF835D 題目連結 題目 略,請看原題 前言 想不到這種狀態... @copy ...

  2. 阿里P6晋升到P7是一个坎吗? P7 晋升总结

    作者:程序之心丁仪 来源:https://chengxuzhixin.com/blog/post/P6_jin_sheng_dao_P7_zong_jie.html 公众号停更了挺长一段时间,首先说声 ...

  3. 虚拟化下Centos7 扩容根分区

    查看分区大小和挂载情况 用到的命令df.lsblk [root@localhost ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/ ...

  4. C#+Arduino Uno 实现声控系统完全实施手册

    话不多说先上视频,一看就懂 另外可参考这里:https://www.cnblogs.com/dehai/p/4285749.html ,这个近6年前的帖子 程序结构 程序分成上位机(PC端)与下位机( ...

  5. Js模块化开发的理解

    Js模块化开发的理解 模块化是一个语言发展的必经之路,其能够帮助开发者拆分和组织代码,随着前端技术的发展,前端编写的代码量也越来越大,就需要对代码有很好的管理,而模块化能够帮助开发者解决命名冲突.管理 ...

  6. 微信小程序——【百景游戏小攻略】

    微信小程序--[百景游戏小攻略] 本次课程小项目中的图片以及文章还未获得授权!请勿商用!未经授权,请勿转载! 博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE ...

  7. 配置交换机Trunk接口流量本地优先转发(集群/堆叠)

    组网图形 Eth-Trunk接口流量本地优先转发简介 在设备集群/堆叠情况下,为了保证流量的可靠传输,流量的出接口设置为Eth-Trunk接口.那么Eth-Trunk接口中必定存在跨框成员口.当集群/ ...

  8. tensorflow的广播机制

    TensorFlow支持广播机制(Broadcast) TensorFlow支持广播机制(Broadcast),可以广播元素间操作(elementwise operations).正常情况下,当你想要 ...

  9. jQuery JSONP

    同源策略 浏览器不允许跨域发送Ajax请求,如你在http://127.0.0.1:8000/下发送一个Ajax请求去获取http://127.0.0.1:9000/的资源. 如何解决这个问题?其实j ...

  10. Spark架构与原理这一篇就够了

    一.基本介绍 是什么? 快速,通用,可扩展的分布式计算引擎. 弹性分布式数据集RDD RDD(Resilient Distributed Dataset)弹性分布式数据集,是Spark中最基本的数据( ...