首页 > 其他分享 >主席树学习笔记

主席树学习笔记

时间:2023-06-13 09:55:06浏览次数:45  
标签:le log dfrac 笔记 学习 索引 数组 节点 主席

什么是主席树

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

什么是可持久化

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

引入例题

洛谷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\),因为它可以用线段树维护。

算法

原理

我们想要支持回退操作就需要对每一次修改操作都进行一次复制,将一些未进行操作也进行复制,这样就可以访问到旧版本的线段树了。
那我们来分析一下单点修改时那些需要复制:
image
所以每一次修改我们只需要修改 \(\log n\) 个点,像这样:
image
每一次都只修改被影响的点就可以。
从这张图中,我们就发现主席树的一些性质:

  • 增加的非叶结点的儿子一个是其他版本的节点,一个是新节点。
  • 主席树有很多根
  • 对于每一个根下面都是一棵完整的线段树
  • 节点都有可能有很多父节点

具体实现

  • 每次增加新节点直接开一块空间新节点,编号为总节点数个数+1
  • 用结构体来存子节点编号
  • 访问子节点时,不是像线段树一样乘2或乘2+1,而是在结构体存子节点编号
  • 每次新开个数组存根。

模版代码(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;
}

标签:le,log,dfrac,笔记,学习,索引,数组,节点,主席
From: https://www.cnblogs.com/OIerBoy/p/17410228.html

相关文章

  • 从今天起,换一种轻松有趣的方式学习计算机底层技术!
    大家好,我是轩辕之风。告诉大家一个好消息,我的 《趣话计算机底层技术》 系列技术故事图书终于出版了! 印刷厂新鲜出炉的第一批图书,已经上线京东、当当啦! 你还记得那个CPU一号车间的阿Q吗?这一次它要继续讲故事给你听啦!创作起源我为什么要写这本书呢?在很多年前,我就发现......
  • node express mvc router 简单目录结构笔记
          只用来参考的  app.jsconstexpress=require('express');constmorgan=require('morgan');consttourRouter=require('./routes/tourRoutes');constuserRouter=require('./routes/userRoutes');constapp=e......
  • html第一天学习
    html标签标题标签:<h1>.....<h6>,特点:文字加粗,独占一行,字号逐渐减小<h1>一般用一次段落标签:<p>换行标签:<br>水平标签:<hr>格式化标签</b>加粗:<strong>倾斜:<em>下划线:<ins>删除线:<del>图片:<imgsrc=""alt=""t......
  • 小灰灰深度学习day9——多线程读取小批量数据(这里运行的时候报错了,目前还不会解决,
    在这里先把代码放上来importtorchimporttimeimportnumpyasnpimporttorchvisionfromtorch.utilsimportdatafromtorchvisionimporttransformsfromd2limporttorchasd2ld2l.use_svg_display()#利用svg显示图片importosos.environ["KMP_DUPLICATE_LIB_OK......
  • 6.12 vue3的学习
    1.创建vue3项目:在cmd中首先找到需要保存的路径,输入vuecreate+vue项目的取名,和之前创建vue2是一样的进行如下选择 2.vite创建vue3的方式在cmd中首先输入npminitvue@latest 3.安装依赖和运行依赖#安装依赖npminstall##运行依赖npmrundev#4.vue2创建app实......
  • ES学习笔记--索引库的操作
    mapping属性mapping是对索引库中文档的约束,常见的mapping属性包括:type:字段数据类型,字符串:text(可分词的文本),keyword(精确值,例如:品牌,国家,IP地址)数值:long,integer,short,byte,double,float布尔:boolean日期:date对象:objectindex:是否......
  • Vue项目学习
    Vue学习笔记一、二维数组尝试varvm=newVue({ el:"#app", data:{ huilv:[ [6.8540,132.9787,1298.7013,1.3278], [6.8540,132.9787,1298.7013,1.3278] ],}二、watch监听实现watch:{ first:function(newValue){ this.second=newValue*this.hui......
  • 2023/6/12日学习笔记
    堆在STL中可以用优先队列来构造使用堆std::priority_queue<int,std::vector<int>>q;//大根堆std::priority_queue<int,std::vector<int>,std::greater<int>>q;//小根堆push()     将元素插入优先队列。pop()      将优先级最顶层的元素从......
  • ES学习笔记--IK分词器
    IK分词器的安装:我这里是采用在线安装的方式:#进入容器内部dockerexec-itelasticsearch/bin/bash#在线下载并安装./bin/elasticsearch-plugininstallhttps://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.14.0/elasticsearch-anal......
  • Transformer架构:革命性的深度学习模型概述
    Transformer架构是一种革命性的深度学习模型,由Vaswani等人在2017年的论文《AttentionisAllYouNeed》中提出。它在自然语言处理(NLP)和其他序列到序列(seq2seq)任务中取得了显著的突破,成为目前最受关注和广泛应用的模型之一。背景与动机在传统的序列模型中,如循环神经网络(RNN)和卷......