主席树(可持久化线段树)入门详解

可持久化线段树(主席树)详解(c++ 图片演示+ 代码) 对于一颗权值线段树,我们要往里面添加n个数字,我们知道,这很容易实现,只需要一个for循环,然后调用n次update函数即可(update函数具有添加元素的功能,详见《权值线段树博文》)。,以便于我们对历史的操作,那么如果只使用权值线段树,则我们需要n个权值线段树同时保存状态,对于非常多的元素,我们就需要。我们前面提到过,每一个根可以组成一个单独的线段树,而每一个根节点都可以用一个唯一的编号表示。这种问题具有非常明显的优势,前面我们说过权值线段树也可以处理区间第k大,但是权值线段树必须是。 阅读详情

一.问题引入.

当我们遇到要求快速查询区间的问题时,通常用线段树、平衡树等数据结构维护这类问题.但是使用这些数据结构的前提是要求区间信息可以快速合并(例如 O ( 1 ) O(1) O(1)或 O ( log ⁡ n ) O(\log n) O(logn)合并),还有很多问题是不可以这样做的,例如区间第k小的问题.

区间第k小问题可以使用很多数据结构来解决,例如划分树、归并树、分块、树套树等数据结构来解决,然而这些数据结构一次查询的时间复杂度大多劣于 O ( log ⁡ n ) O(\log n) O(logn).

就当这些数据结构都对这类问题显得十分无力的时候,可持久化这种数据结构上的技巧横空出世,而主席树(可持久化线段树)就是其中的一种经典应用.


二.权值线段树.

在认识主席树之前,我们先来看一种特殊的线段树——权值线段树,这种线段树的维护的是序列的数值范围内每一个值的出现情况.

我们不考虑时空复杂度,尝试利用权值线段树来维护区间第 k k k小问题.

首先建立 n n n棵结构相同线段树,每棵线段树维护区间 [ 1 , n ] [1,n] [1,n]中出现的权值信息.那么当我们要查询区间 [ l , r ] [l,r] [l,r]的第 k k k小数时,就可以同时从维护 [ 1 , l − 1 ] [1,l-1] [1,l−1]和 [ 1 , r ] [1,r] [1,r]的线段树的根节点出发往下走.设 t r [ m ] [ i ] tr[m][i] tr[m][i]表示维护 [ 1 , m ] [1,m] [1,m]的线段树的第 i i i个节点,那么若 t r [ r ] [ i ] tr[r][i] tr[r][i]的左儿子所包含的权值数量减去 t r [ l − 1 ] [ i ] tr[l-1][i] tr[l−1][i]左儿子所包含的权值数量小于 k k k则往右儿子走(注意让 k k k减掉权值数量),否则往左儿子走.

这个时候我们会发现查询一次的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),但是空间复杂度和建树的时间复杂度均为 O ( n 2 ) O(n^2) O(n2)(离散化后),于是我们的算法就要考虑如何优化这一复杂度.


三.可持久化的引入.

我们设原来的权值序列为 2 , 1 , 3 , 4 2,1,3,4 2,1,3,4,那么原来的 4 4 4棵线段树大概是这样的:
在这里插入图片描述
其中橙色的节点表示与上一棵线段树之间的差异.

我们考虑相邻的两棵线段树之间的关系,发现两棵线段树之间的差异只有树上的一条长度为 O ( log ⁡ n ) O(\log n) O(logn)的链!

这是否可以启发我们可以每次只存储发生改变的节点,其它节点直接调用上一棵的?

考虑这样子存储原来的 n n n棵线段树:
在这里插入图片描述
图有点丑不要介意QAQ.

我们发现这正好对应了我们上面的只存修改节点的思想,看起来这个想法非常可行,显然时空复杂度均为 O ( n log ⁡ n ) O(n\log n) O(nlogn).

事实上,这是一种被称作“可持久化”的技巧,这种技巧被广泛应用于数据结构的维护上,我们上面维护的线段树就是可持久化线段树,也被称为主席树.

类比可持久化线段树,我们还可以维护可持久化Trie、可持久化并查集、可持久化平衡树等数据结构,除了可持久化树状数据结构外,也有可持久化栈、可持久化块状链表等数据结构.


四.可持久化线段树的实现.

说了那么多,我们该如何实现一棵可持久化线段树呢?

首先我们可以仿照动态开点线段树,初始第一棵树直接利用 0 0 0节点的自我迭代来实现,也就是说我们不需要建树.

之后考虑如何往后增加一棵树 k k k.其实很简单,只需要顺着第 k − 1 k-1 k−1棵树往下,新建一条链即可,代码如下:

void Add_tree(int x,int l,int r,int hk,int &k){
  tr[k=++cn]=tr[hk];
  if (l==r) {++tr[k].sum;return;}
  int mid=l+r>>1;
  if (x<=mid) Add_tree(x,l,mid,tr[hk].s[0],tr[k].s[0]);
  else Add_tree(x,mid+1,r,tr[hk].s[1],tr[k].s[1]);
  Pushup(k);
}
//主程序中
Add_tree(a[i],1,n,rot[i-1],rot[i]);

查询 [ l , r ] [l,r] [l,r]就从第 l − 1 l-1 l−1和第 r r r往下走就好了,代码如下:

int Query(int k,int l,int r,int L,int R){
  if (l==r) return l;
  int sum=tr[tr[R].s[0]].sum-tr[tr[L].s[0]].sum,mid=l+r>>1;
  if (sum>=k) return Query(k,l,mid,tr[L].s[0],tr[R].s[0]);
  else return Query(k-sum,mid+1,r,tr[L].s[1],tr[R].s[1]);
}



五.例题与代码.

题目:luogu3834.

注意,这题由于数的范围太大权值线段树开不下,需要离散化.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
 
#define Abigail inline void
typedef long long LL;

const int N=200000,C=30; 

int n,m,a[N+9],ord[N+9];

int Lower(int *a,int n,int x){
  int l=1,r=n,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    x>a[mid]?l=mid+1:r=mid;
  return l;
}

struct tree{
  int s[2],sum;
}tr[N*C+9];
int cn,rot[N+9];

void Pushup(int k){tr[k].sum=tr[tr[k].s[0]].sum+tr[tr[k].s[1]].sum;}

void Add_tree(int x,int l,int r,int hk,int &k){
  tr[k=++cn]=tr[hk];
  if (l==r) {++tr[k].sum;return;}
  int mid=l+r>>1;
  if (x<=mid) Add_tree(x,l,mid,tr[hk].s[0],tr[k].s[0]);
  else Add_tree(x,mid+1,r,tr[hk].s[1],tr[k].s[1]);
  Pushup(k);
}

int Query(int k,int l,int r,int L,int R){
  if (l==r) return l;
  int sum=tr[tr[R].s[0]].sum-tr[tr[L].s[0]].sum,mid=l+r>>1;
  if (sum>=k) return Query(k,l,mid,tr[L].s[0],tr[R].s[0]);
  else return Query(k-sum,mid+1,r,tr[L].s[1],tr[R].s[1]);
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i){ 
    scanf("%d",&a[i]);
    ord[i]=a[i];
  }
}

Abigail work(){
  sort(ord+1,ord+1+n);
  for (int i=1;i<=n;++i)
    a[i]=Lower(ord,n,a[i]);
  for (int i=1;i<=n;++i)
    Add_tree(a[i],1,n,rot[i-1],rot[i]);
}

Abigail getans(){
  int l,r,k;
  while (m--){
  	scanf("%d%d%d",&l,&r,&k);
  	printf("%d\n",ord[Query(k,1,n,rot[l-1],rot[r])]);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}
可持久化线段树(主席树) 摘要 主席树,又称可持久化线段树,属于可持久化数据结构。“主席”这一名词是由于发明者缩写为HJT,和某位主席拼音缩写相同(有些牵强),故将该数据结构称为主席树。 主席树既保留了线段树的灵活,也拥有了可持久化数据结构的特点,在处理某些特定问题时有着其它数据结构不具有的优势。 本文将首先介绍什么是“可持久化数据结构”,随后介绍主席树的思想,关于代码实现将结合例题讲解。 可持久化数据结构 可持久数据结构... 阅读详情

相关推荐

可持久化专题(一)——浅谈主席树:可持久化线段树

前言 不得不说,可持久化数据结构真是太难了! 由于数据结构这东西真的太玄学了,学这个主席树我真的学了很久。 简介 主席树为什么叫主席树?据说因为它是一个名字缩写为HJTHJTHJT的神犇发明的,与当时主席的名字缩写一样…… 主席树实质上就是一棵可持久化线段树,它的具体实现可以看下面。 让我们从值域线段树开始说起 要学主席树,我们就要先学值域线段树。 值域线段树的区间存的并不...

CXR的博客 6457

主席树详解(基础)

可持久化线段树,又称主席树,本文详解它的构成和基础用法

nxhx_的博客 4661

IEEE论文接收后,收到Proof邮件别慌!手把手教你48小时内搞定校样(附详细截图)

本文详细解析了IEEE论文接收后Proof校样的全流程,从邮件关键信息解码到精准修改标注,提供48小时内高效完成校样的实战指南。涵盖登录技巧、文件验证、修改标注黄金法则及九宫格检查清单,帮助作者避免常见错误,确保论文顺利发表。

weixin_30652491的博客 563

详解——主席树

由来 主席树又称函数式线段树,顾名思义,也就是通过函数来实现的线段树 然而我并不知道什么是函数式线段树,某位大爷说:“主席树与可持...

weixin_30657541的博客 388

CiteSpace基础操作(CNKI数据的导入分析)(一)

1 Cite Space简单介绍 2 文献导入 2.1 打开知网,选择高级检索

m0_50990607的博客 5万+

OpenBCI脑电实验避坑指南:如何用Python准确捕捉P300信号

本文详细介绍了使用OpenBCI和Python进行P300脑电信号实验的避坑指南,包括电极贴放、信号采集、信号处理和数据分析等关键步骤。通过实战经验和代码示例,帮助研究者避开常见陷阱,提高P300信号捕捉的准确率,适用于脑机接口和神经科学研究。

weixin_30316097的博客 551

主席树详解

主席树 抛出问题 给定NN个数(intint范围内),一共MM次询问,每次都要询问区间[l,r][l,r]的第kk大的数。  其中N,M,l,rN,M,l,r均不超过2×1052×105,保证询问有答案。 解决问题 暴力法 显而易见,最暴力的办法就是区间排序然后输出排序后第kk个数。最坏情况的时间复杂度是O(nmlgn)O(nmlgn),不超时才怪。 主席树(可持久化线段树)法 于是...

西红柿爱炒番茄 453

CH341的I2C流模式(StreamI2C)到底怎么用?从EEPROM读写到自定义设备,一篇讲清参数配置逻辑

本文详细解析了CH341的I2C流模式(StreamI2C)的使用方法,从EEPROM读写到自定义设备的参数配置逻辑。通过深入分析I2C协议基础、StreamI2C参数映射关系及典型设备配置示例,帮助开发者快速掌握StreamI2C的应用技巧,解决实际开发中的常见问题。

weixin_30932215的博客 448

主席树

抛出问题 给定\(N\)个数(\(int\)范围内),一共\(M\)次询问,每次都要询问区间\([l,r]\)的第\(k\)大的数。 其中\(N,M,l,r\)均不超过\(2\times 10^5\),保证询问有答案。 点我去模板题 解决问题 暴力法 显而易见,最暴力的办法就是区间排序然后输出排序后第\(k\)个数。最坏情况的时间复杂度是\(O(nm\lg n)\),不超时才怪。 主席树...

GKX328131442的博客 601

【阿里云大模型高级工程师ACP习题集】2.9 大模型应用生产实践(下篇)

【摘要】这是我精心整理出对应的习题集,里面足足有 30 道题目,从基础概念到深度拓展,全方位覆盖章节重点、难点。不管你是想检验自己的学习成果,还是想针对薄弱环节进行强化训练,它都能完美适配!

davidkorhenn的博客 2237

手把手教你学Simulink--基于新能源并网与协调控制的场景实例:双馈异步风力发电机并网运行仿真

随着“双碳”目标的推进,风力发电已成为新能源并网的核心组成部分。其中,双馈异步风力发电机(Doubly-Fed Induction Generator, DFIG)因其变频器容量小(仅处理转差功率)、可实现有功/无功独立调节、运行效率高等优势,广泛应用于现代兆瓦级风电机组。DFIG系统通过转子侧变流器(Rotor-Side Converter, RSC)和网侧变流器(Grid-Side Converter, GSC)实现对发电机转矩与电网无功的精确控制,支持最大功率点跟踪(MPPT)和。

MHD0815的博客 280

主席树 可持久化线段树基础知识入门详解

主席树 可持久化权值线段树,又称为主席树。是权值线段树的进阶版,如果不会权值线段树可以先学习一下。 【权值线段树】基础入门知识详解 是什么 它可以看作是多棵权值线段树,但它所占的空间很小!!! 具体不容易解释,可以先往后面的内容浏览。 为什么要用它 对于一棵权值线段树,我们要往里面加入nnn个数。容易知道,每加入一个数就会更新一遍线段树。 当我们想要知道每次更新后的权值线段树的状态...

ZSJZ_liuzian的博客 1859

图神经网络实战——总结 | 图神经网络展望

短短数年时间,图神经网络 (Graph Neural Networks, GNN) 已经成为一种重要的深度学习架构,并且已经广泛应用于各个行业。通过本专栏的学习,能够对图学习和 GNN 领域有一个全面的了解,并能为各种应用设计、构建GNN模型。

盼小辉丶的博客 8551

可持久化线段树 主席树 详解

可持久化线段树 主席树 详解 一、可持久化线段树 简介 可持久化线段树,顾名思义,即对线段树进行可持久化处理之后的线段树。 在可持久化数据结构的理论中,我们对可持久化的概念有所了解:“可以返回之前的某个状态,并在该基础上进行修改”。

HeartFireY的博客 2417

大学计算 - NumPy库的矩阵运算 实训答案

本文介绍了三个NumPy矩阵操作的实验练习。第一关练习ndarray类型操作,包括矩阵构造、元素平方和计算、矩阵乘积运算和逆矩阵行列式计算;第二关针对matrix类型,涉及绝对值最大值获取、特定矩阵乘积运算和转置矩阵特征值计算;第三关实现Moore-Penrose伪逆矩阵计算,并与NumPy内置pinv函数结果比较误差。实验涵盖了NumPy核心矩阵操作,展示了从基础运算到高级矩阵分解的应用,帮助掌握科学计算中的矩阵处理技巧。

2403_89314916的博客 310

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

[C++]可持久化线段树 主席树模型分析及例题详解

可持久化线段树 主席树模型分析及例题详解

weixin_62712365的博客 2245

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

可持久化线段树【主席树】详解

首先科普下为什么叫主席树呢,这是因为主席树的创始人是一位叫做黄嘉泰的大佬,然后这位大佬名字的缩写是HJT,和某位伟人的名字缩写一样....haha,所以就有了主席树这个说法啦。 其实网上已经有很多博主写了关于主席树详解的博客了,但是我自己为什么还要手敲这样一份主席树教学呢?因为我在刚学主席树的时候,也是看了很多博主写的入门教程,但是没有任何单独一份就能让我搞明白什么是主席树,让我吃了很多苦头,所...

KnightHONG的博客 1795

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

主席树(可持久化线段树)

2019.11.4 今天总算弄会主席树了,发现确实不难,但是需要跳出常规线段树的思维,特别是处理子树上面 主席树的定义与作用 主席树,也称可持久化线段树,那么什么是可持久化线段树呢,即为一颗记录了所有更新过程的线段树。能够处理出从第i次更新到第j次更新的线段树变化,具体作用一会见例题。 主席树的功能 记录所有过程的线段树,按正常思维是需要开O(N*M)这么大的空间的,时间也为O(n*m),...

孙煜晗的博客 7009

【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

你会抄作业吗?巧了,我正在这么干

1202

权值线段树、主席树学习

初学主席树,主要是反复看了卿学姐的视频(我竟然在B站学算法)和知乎“主席树是如何求区间k大的”,才算懂了点皮毛。 传送门: 卿学姐的B站视频 知乎-“主席树是如何求区间k大的” 首先,学习主席树要点的前置技能是权值线段树(卿学姐说的是线段树,个人认为不太确切)。权值线段树之所以会带上“权值”二字,是因为它是记录权值的线段树。因此需要用到离散化操作来处理a[1-n]。记录权值指的是,每...

Stupid_Turtle的博客 9692

可持久化线段树——主席树

文章目录 可持久化前提:本身的拓扑结构不变

Cauchy的博客 962

可持久化数据结构-线段树(主席树)

可持久化线段树的概念和简单运用

zje11的博客 2741

可持久化线段树(主席树) 详解

这个数据结构可以在OlogN解决区间第k小值查询问题。

2401_89382924的博客 972

[note]可持久化权值线段树(主席树)算法学习笔记

学习主席树,你需要先掌握:线段树概念、写法权值线段树离散化一道经典的问题是:给定 n个整数构成的序列,将对于指定的闭区间 [l,r] 查询其区间内的第 k小值。我们发现因为是k小值所以ST表失效无法使用,普通线段树也无法维护区间第k小的值,我们可以用可持久化权值线段树(主席树来解决这个问题。

weixin_43720735的博客 2226

可持久化线段树/主席树

具体来说,可持久化线段树是可以保存所有历史版本的线段树,每次插入,都会创建一颗新树,保存这次插入后版本的线段树,返回这个版本的线段树根节点,后面想查询一个历史版本时,就使用这个版本的根节点,进行线段树查询就行了,具体查询和线段树正常查询完全一样。个点,线段树中剩余的点其实完全没有变化,那其实我们可以在每次插入时,只对受到影响的点创建新的版本,然后接到未受影响的点上,这样每次插入,创建新版本的复杂度就是。小于左子树的元素个数,就进入左子树,否则进入右子树,知道来到叶节点,就是我们要找的元素的值。

Maxwell_Newton的博客 946
上一篇: hdu6390 GuGuFishtion 题解(Mobius反演+欧拉函数+除法分块)
下一篇: Codeforces 235 E Number Challenge 题解(Mobius反演)
hezlik
博客等级 码龄9年 111粉丝 241原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值