主席树学习笔记

这篇具有很好参考价值的文章主要介绍了主席树学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是主席树

主席树这个名字看上去很高级,其实不然,它还有另一个名字——可持久化线段树。

什么是可持久化

可持久化顾名思义就是它可以变得持久,就是我们对他不断进行单点修改后,突然查询它的某一个历史版本,这就叫可持久化。

引入例题

洛谷3919:可持久化数组

题目大意

如题,你需要维护这样的一个长度为 \(N\ (1\le N\le 10^6)\) 的数组,支持如下几种操作

  • 1.在某个历史版本上修改某一个位置上的值

  • 2.访问某个历史版本上的某一位置的值

此外,每进行一次操作(对于操作 2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从 1 开始编号,版本 0 表示初始状态数组)

此时我们就需要用到主席树了。

分析

问题:这里我们为什么不能直接用数组来做?

很简单,如何我们用数组来做的话每改变一个数,我们就要新建一个数组并将其他没有改变的也一起复制下来,而\(1\le N \le 10^6\) 空间不支持(如果可以就没必要做了)

思考:问什么明明只修改了一个数,却必须将其他的数也复制一遍?

那是因为数组的一级索引所决定的。

什么是索引

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。

最优索引

问题:怎样的索引是最高效呢?

这里,我们设索引大小为 \(k\),那么索引的层数为 \(\log_kn\),则每次修改的数量为 \(k\log_kn\)

\(k\log_kn=\log_kn^k=k\dfrac{\log n}{\log k}=\log n\dfrac{k}{\log k}\)

\(f(n)=\dfrac{k}{\log k}\)

\(f'(n)=\dfrac{\log k+1}{\log^2 k}=\left(\dfrac{1}{\log k}\right)^2+\dfrac{1}{\log k}\)

\(1\le k\le n\) 的情况下,\(\log k\in\left[0,\infty\right]\)

所以 \(k_{\min}=e\ (\log k=1)\)

即索引为 \(k=2\)\(k=3\) 时最优。

这里我们取 \(k=2\),因为它可以用线段树维护。

算法

原理

我们想要支持回退操作就需要对每一次修改操作都进行一次复制,将一些未进行操作也进行复制,这样就可以访问到旧版本的线段树了。

那我们来分析一下单点修改时那些需要复制:

主席树学习笔记

所以每一次修改我们只需要修改 \(\log n\) 个点,像这样:

主席树学习笔记

每一次都只修改被影响的点就可以。

从这张图中,我们就发现主席树的一些性质:

  • 增加的非叶结点的儿子一个是其他版本的节点,一个是新节点。

  • 主席树有很多根

  • 对于每一个根下面都是一棵完整的线段树

  • 节点都有可能有很多父节点

具体实现

  • 每次增加新节点直接开一块空间新节点,编号为总节点数个数+1

  • 用结构体来存子节点编号

  • 访问子节点时,不是像线段树一样乘2或乘2+1,而是在结构体存子节点编号

  • 每次新开个数组存根。文章来源地址https://www.toymoban.com/news/detail-480788.html

模版代码(P3919)

#include<bits/stdc++.h>
#define Mod 1000000007
#define int long long
#define For(i,j,k) for(int i=j;i<=k;++i)
#define FOR(i,j,k) for(int i=j;i>=k;i--)
#define mid ((l+r)>>1)
#define N 1000006
using namespace std;
int a[N],n,m,Q,root[N<<5];
struct PST{
	int lc[N<<5],rc[N<<5],val[N<<5],cnt;
	void build(int &x,int l,int r){
		x=++cnt;
		if(l==r){
			val[x]=a[l];
			return ;
		}
		build(lc[x],l,mid);
		build(rc[x],mid+1,r);
	}
	void ins(int &x,int k,int l,int r,int L,int R){
		x=++cnt;
		lc[x]=lc[k];
		rc[x]=rc[k];
		val[x]=val[k];
		if(l==r){
			val[x]=R;
			return ;
		}
		if(L<=mid)ins(lc[x],lc[k],l,mid,L,R);
		else ins(rc[x],rc[k],mid+1,r,L,R);
	}
	int query(int x,int l,int r,int k){
		if(l==r)return val[x];
		if(k<=mid)return query(lc[x],l,mid,k);
		else return query(rc[x],mid+1,r,k);
	}
}T;
signed main(){
	cin>>n>>m;
	For(i,1,n)cin>>a[i];
	T.build(root[0],1,n);
	For(i,1,m){
		int k,opt,x,y;
		cin>>k>>opt>>x;
		if(opt==1){
			cin>>y;
			T.ins(root[i],root[k],1,n,x,y);
		}
		if(opt==2){
			cout<<T.query(root[k],1,n,x)<<endl;
			root[i]=root[k];
		}
	}
	return 0;
}

到了这里,关于主席树学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Hessian 矩阵汉语叫什么名字,是什么意思,是用来干什么的?

    问题描述:Hessian 矩阵汉语叫什么名字,是什么意思,是用来干什么的? 问题解答: Hessian 矩阵的汉语名字是“黑塞矩阵”或“海森矩阵”。 这个名字的来源是对德国数学家Ludwig Hessian(海森)的姓氏的翻译。Hessian 矩阵是一个方阵,其中的元素是一个函数的二阶偏导数,用

    2024年01月22日
    浏览(51)
  • 主席树的简要讲解

    区间第k小值 主席树是解决动态查找序列上[l,r]区间中的第k小值的一个数据结构 核心思想:动态开点(后面会讲) 传统线段树都是值域线段树 其实意思就是每个节点都存的是序列上[l,r]的一个区间和,但是考虑我们需要动态处理区间的不是最值,故换一种线段树 主席树一般用

    2024年04月23日
    浏览(24)
  • 为什么边缘正在“吞噬”这个世界

    10多年前,马克·安德烈森在《华尔街日报》上发表了他的著名文章《为什么软件正在“吞噬”世界》。他从投资者的角度,解释了为什么软件公司正在接管全部行业。 作为一名公司创始人,我们公司的业务就是在边缘支持GraphQL,因此,我想就为什么边缘正在“吞噬”整个世

    2024年01月17日
    浏览(50)
  • 一. 为什么需要云计算这个技术

    第一次工业革命、第二次工业革命、第三次工业革命等等,随着时代的发展进步,对计算能力的要求不断提高。 云计算的能力:计算能力、存储能力、网络能力、安全能力 大数据和人工智能都需要依托云计算的基础设施来运行 涉及领域:政府、工业、交通、物流、医疗健康

    2024年01月24日
    浏览(56)
  • 机器学习笔记 - 什么是多模态深度学习?

            人类使用五种感官来体验和解释周围的世界。我们的五种感官从五种不同的来源和五种不同的方式捕获信息。模态是指某事发生、经历或捕捉的方式。         人工智能正在寻求模仿人类大脑,终究是跳不出这具躯壳的限制。         人脑由可以同时处理

    2024年02月09日
    浏览(40)
  • 什么是“网络空间安全”?这个行业就业方面如何?

    网络空间安全指的是保护网络系统、设备、程序和数据免受未经授权的访问、破坏、篡改、泄露和滥用等各种威胁和风险的能力。它是保障国家安全、社会稳定、经济发展和个人隐私安全的重要方面。 网络空间安全主要涉及以下几个方面: 保护网络系统和设备的安全:网络

    2024年02月10日
    浏览(56)
  • <dependency> idea中为什么这个变黄色

      在IDE中,当你的代码出现黄色高亮时,通常表示存在警告或建议的提示。对于Maven的 dependency 标签来说,黄色高亮可能有以下几种原因: 依赖项未找到:黄色高亮可能表示IDE无法找到指定的依赖项。这可能是由于配置错误、网络问题或仓库中缺少该依赖项等原因导致的。你

    2024年02月14日
    浏览(41)
  • openGauss学习笔记-01 什么是openGauss

    openGauss学习笔记-01 什么是openGauss openGauss是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。openGauss深度融合华为在数据库领域多年的研发经验,结合企业级场

    2024年02月12日
    浏览(33)
  • Pycharm这个更新索引是个什么操作,为什么每次启动,都会进行?

    点击上方“ Python爬虫与数据挖掘 ”,进行关注 回复“ 书籍 ”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 九重城阙烟尘生,千乘万骑西南行。 大家好,我是皮皮。 一、前言 前几天在Python最强王者交流群【吴超建】问了一个 Pycharm 操作的问题,这里拿出来给大家分

    2024年02月01日
    浏览(56)
  • (学习笔记-进程管理)什么是悲观锁、乐观锁?

    最底层的两种就是 [互斥锁和自旋锁],有很多高级的锁都是基于它们实现的。可以认为它们是各种锁的地基,所以我们必须清楚它们之间的区别和应用。 加锁的目的就是保证共享资源在任意时间内,只有一个线程访问,这样就可以避免多线程导致共享数据错乱的问题。 当已

    2024年02月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包