【数据结构模版】可持久化线段树 && 主席树
浙江集训Day4,从早8:00懵B到晚21:00,只搞懂了可持久化线段树以及主席树的板子。今天只能记个大概,以后详细完善讲解。
可持久化线段树指的是一种基于线段树的可回溯历史状态的数据结构。我们想要保存某一段序列的历史信息,可以朴素地开m倍的空间存储;而线段树是一种优秀的数据结构,它每次修改操作只把原先的数据修改log个节点,这就意味着我们只增加这些被修改的节点存储新信息就好了。传统的线段树用二叉树存储,但是因为涉及加点,可持久化线段树必须维护一个自增的tot动态成为新点的编号。对于每个节点,我们要维护两个指针指向它的左右子节点。对于每次修改,我们把新节点的不被修改的一半区间指向上个状态该区间的节点编号,等同于利用了上次的区间信息;另一半新开一个点继续递归下去即可。
可持久化线段树一般不能维护区间修改,因为多个根共享子节点,我们没有办法对每个状态的树独立维护它的lazy_tag。
代码(动态开点&&区间最大值):
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <climits>
- #define maxn 1000100
- #define maxm 1000100
- #define LG 21
- #define BUG putchar('*')
- using namespace std;
- template <typename T>
- void read(T &x) {
- x = 0;
- int f = 1;
- char ch = getchar();
- while (!isdigit(ch)) {
- if (ch == '-')
- f = -1;
- ch = getchar();
- }
- while (isdigit(ch)) {
- x = x * 10 + (ch ^ 48);
- ch = getchar();
- }
- x *= f;
- return;
- }
- int n, m, a[maxn];
- namespace Segment_tree {
- int root[maxm], tot;
- struct node {
- int dat, lc, rc;
- } seg[maxn * 20];
- inline void update(int nd) {
- seg[nd].dat = max(seg[seg[nd].lc].dat, seg[seg[nd].rc].dat);
- }
- int build(int l, int r) {
- int nd = ++tot;
- if (l == r) {
- seg[nd].dat = a[l];
- return nd;
- }
- int mid = (l + r) >> 1;
- seg[nd].lc = build(l, mid), seg[nd].rc = build(mid + 1, r);
- update(nd);
- return nd;
- }
- int modify(int nd, int l, int r, int x, int val) {
- int now = ++tot;
- seg[now] = seg[nd];
- if (l == r) {
- seg[now].dat = val;
- return now;
- }
- int mid = (l + r) >> 1;
- if (x <= mid)
- seg[now].lc = modify(seg[nd].lc, l, mid, x, val);
- else seg[now].rc = modify(seg[nd].rc, mid + 1, r, x, val);
- update(now);
- return now;
- }
- int query(int nd, int l, int r, int ql, int qr) {
- if (ql <= l && qr >= r)
- return seg[nd].dat;
- if (ql > r || qr < l)
- return INT_MIN;
- int mid = (l + r) >> 1;
- return max(query(seg[nd].lc, l, mid, ql, qr), query(seg[nd].rc, mid + 1, r, ql, qr));
- }
- } using namespace Segment_tree;
- int main() {
- read(n), read(m);
- for (int i = 1; i <= n; ++i)
- read(a[i]);
- root[0] = build(1, n);
- int k, op, val, loc, l, r;
- for (int i = 1; i <= m; ++i) {
- read(k), read(op);
- if (op == 1) {
- read(loc), read(val);
- root[i] = modify(root[k], 1, n, loc, val);
- } else {
- read(l), read(r);
- printf("%d\n", query(root[k], 1, n, l, r));
- root[i] = root[k];
- }
- }
- return 0;
- }
听说主席树是由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]信息的权值线段树来查询对应区间信息的。
代码:
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <climits>
- #define maxn 200100
- #define maxm 200100
- #define LG 21
- #define BUG putchar('*')
- using namespace std;
- int n, m;
- template <typename T>
- void read(T &x) {
- x = 0;
- int f = 1;
- char ch = getchar();
- while (!isdigit(ch)) {
- if (ch == '-')
- f = -1;
- ch = getchar();
- }
- while (isdigit(ch)) {
- x = x * 10 + (ch ^ 48);
- ch = getchar();
- }
- x *= f;
- return;
- }
- namespace Contra {
- int st[maxn];
- int work(int *org, int *st) {
- for (int i = 1; i <= n; ++i)
- st[i] = org[i];
- sort(st + 1, st + 1 + n);
- int len = unique(st + 1, st + n + 1) - (st + 1);
- for (int i = 1; i <= n; ++i)
- org[i] = lower_bound(st + 1, st + len + 1, org[i]) - st;
- return len;
- }
- }
- int a[maxn], N;
- namespace President_tree {
- #define lc(i) seg[(i)].lc
- #define rc(i) seg[(i)].rc
- using namespace Contra;
- int tot, root[maxm];
- struct node {
- int cnt, lc, rc;
- } seg[maxm * 20];
- void update(int nd) {
- seg[nd].cnt = seg[lc(nd)].cnt + seg[rc(nd)].cnt;
- }
- int build(int l, int r) {//root[0]:empty_tree
- int nd = ++tot;
- if (l == r) return nd;
- int mid = (l + r) >> 1;
- seg[nd].lc = build(l, mid);
- seg[nd].rc = build(mid + 1, r);
- return nd;
- }
- int modify(int pre, int l, int r, int x) {
- int nd = ++tot;
- seg[nd] = seg[pre];
- if (l == r) {
- ++seg[nd].cnt;
- return nd;
- }
- int mid = (l + r) >> 1;
- if (x <= mid)
- lc(nd) = modify(lc(pre), l, mid, x);
- else
- rc(nd) = modify(rc(pre), mid + 1, r, x);
- update(nd);
- return nd;
- }
- int query(int ql, int qr, int l, int r, int k) {
- if (l == r) {
- return st[l];
- }
- int mid = (l + r) >> 1, sum = seg[lc(qr)].cnt - seg[lc(ql)].cnt;
- if (k <= sum)
- return query(lc(ql), lc(qr), l, mid, k);
- return query(rc(ql), rc(qr), mid + 1, r, k - sum);
- }
- void init() {
- N = work(a, st);
- root[0] = build(1, N);
- for (int i = 1; i <= n; ++i) {
- root[i] = modify(root[i - 1], 1, N, rk[i]);
- }
- }
- } using namespace President_tree;
- int main() {
- read(n), read(m);
- for (int i = 1; i <= n; ++i)
- read(a[i]);
- init();
- int l, r, k;
- for (int i = 1; i <= m; ++i) {
- read(l), read(r), read(k);
- printf("%d\n", query(root[l - 1], root[r], 1, N, k));
- }
- return 0;
- }
【数据结构模版】可持久化线段树 && 主席树的更多相关文章
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- [学习笔记] 可持久化线段树&主席树
众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...
- 权值线段树&&可持久化线段树&&主席树
权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...
- 洛谷P3834 可持久化线段树(主席树)模板
题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...
- bzoj 4408: [Fjoi 2016]神秘数 数学 可持久化线段树 主席树
https://www.lydsy.com/JudgeOnline/problem.php?id=4299 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1 ...
- 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)
J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...
- [POJ2104] K – th Number (可持久化线段树 主席树)
题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
随机推荐
- Foundation 用于开发响应
Foundation 用于开发响应式的 HTML, CSS and JavaScript 框架. Foundation 是一个易用.强大而且灵活的框架,用于构建基于任何设备上的 Web 应用. Fou ...
- java数据结构-12树相关概念
一.树 1.概念: 包含n(n>=0)个结点的有穷集:树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节 ...
- 如何使用Python爬取基金数据,并可视化显示
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于Will的大食堂,作者打饭大叔 前言 美国疫情越来越严峻,大选也进入 ...
- Reading Face, Read Health论文阅读笔记
摘要: 随着计算技术觉.人工智能和移动技术的发展,利用计算机读脸技术去识别每个人每天的健康是可行的.怎么去设计一个基于FRT(face reading technologies)的用于得到每天的保健实 ...
- SourceTree安装及卸载
一.安装步骤 下载地址: 链接:https://pan.baidu.com/s/1K5ImZASuThJZoGLz6Ay_4g 提取码:hqkp 1. 点击安装包,点击下一步 2. 出现账户注册的页面 ...
- Pycharm同步远程服务器调试
Pycharm同步远程服务器调试 1.需要准备工具 xftp:上传项目文件 xshell:连接Linux系统调试,执行命令 PyCharm:调试python代码 这些软件可以自行网上搜索下载,也可以关 ...
- 用spring-retry注解自动触发重试方法
原文地址:https://www.jianshu.com/p/ee02d6125113 需求背景: 有些时候我们再调用一些第三方服务的时候,从第三方那边拉数据. 但是第三方服务不是100%稳定的,有些 ...
- 选择API管理平台之前要考虑的5个因素
API(应用程序编程接口)经济的飞速增长导致对API管理平台的需求相应增加. 这些解决方案可在整个生命周期内帮助创建,实施,监控,分析,保护和管理API. 据一些估计,全球API管理市场预计在2018 ...
- 13、form组件
Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否 ...
- yum源备份并安装扩展仓库
yum源相关 默认的YUM源 1.备份默认的YUM源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.ba ...