可持久化线段树(主席树)快速简洁教程 图文并茂 保证学会。kth number例题
如果学不会也不要打我。
假设你会线段树
开始!
主席树也叫可持久化线段树
顾名思义,它能够保存线段树在每个时刻的版本。
什么叫每个时刻的版本?你可能对一棵普通线段树进行各种修改,这每种样子就是我们所说的不同时刻的版本。
假设我们对线段树进行单点修改,维护区间和。
每次修改操作中,只有logn个节点会被修改,我们可以复制这些被修改的节点,而不复制没有被改变的节点(以提高效率)。
最后通过特殊的方式建立出新时刻的树。
建造方式如下:
假设上一时刻的树长这样:

现在进行修改操作,对下标为3的位置修改,也就是说修改1 3 6号节点。若是普通线段树,则直接修改1 3 6三个节点。但是在主席树中,我们不直接在1 3 6号节点上修改。而是新建3个节点,分别对应1 3 6号节点,对新建节点进行本想在1 3 6号节点进行的操作(“本想”指普通线段树)。
如图:

关于不同版本的线段树理解
接上图,可以观察到,1号和8号往下分别是两棵不同版本的线段树,不同版本共用很多节点。这并不会影响自上而下的查询。
单点修改的例子

以下内容质量不高:
寒羽吾:
用主席树做kth number,就是在空线段树的基础上,依次在线段树的位置a[1]处加一,a[2]处加一。即用线段树维护值在某区间中的ai有多少个。然后可以在线段树上移动指针,找到第k个。
寒羽吾:
考虑区间[l,r]的限制,即r时刻的线段树减去l-1时刻的线段树,就得到维护ai(下标i在[l,r]中)的线段树了。
寒羽吾:
查询的时候不必真把做差得到的线段树求出来,需要这个线段树的什么位置就访问r版本和l-1版本的对应点,取出值相减即可。
寒羽吾:
上面是我写的板子,t表示树节点,w[0]左孩子w[1]右孩子。
寒羽吾:
理论上维护21e9个元素的线段树是开不下节点的(也是时间上不可建立的),但因为主席树的特殊性:只建立需要用(改变)的节点。所以可以不对ai进行离散化,直接建立“看似”能维护21e9个元素的线段树。
#include<bits/stdc++.h>
using namespace std;
struct node{
int sum;
int w[2];
}t[5000005];
int np;
int n,m;
int st[100005];
int a[100005];
void plu(int x,int c,int num){
int now=++np;
t[now]=t[x];
if(c<0){
t[now].sum++;
return;
}
int p=(num>>c)&1;
plu(t[now].w[p],c-1,num);
t[now].w[p]=now+1;
t[now].sum=t[t[now].w[0]].sum+t[t[now].w[1]].sum;
}
int solve(int l,int r,int k){
int lx=st[l-1],rx=st[r],ans=0;
for(int i=0;i<=30;i++){
int p=0;
if(t[t[rx].w[0]].sum-t[t[lx].w[0]].sum<k)
k-=t[t[rx].w[0]].sum-t[t[lx].w[0]].sum,
p=1;
lx=t[lx].w[p],
rx=t[rx].w[p],
ans=ans<<1|p;
}
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]+=1e9;
}
np=1;st[0]=1;
for(int i=1;i<=n;i++){
st[i]=np+1;
plu(st[i-1],30,a[i]);
}
for(int i=1;i<=m;i++){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",int(solve(l,r,k)-1e9));
}
return 0;
}
可持久化线段树(主席树)快速简洁教程 图文并茂 保证学会。kth number例题的更多相关文章
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- 权值线段树&&可持久化线段树&&主席树
权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...
- [学习笔记] 可持久化线段树&主席树
众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...
- 【数据结构模版】可持久化线段树 && 主席树
浙江集训Day4,从早8:00懵B到晚21:00,只搞懂了可持久化线段树以及主席树的板子.今天只能记个大概,以后详细完善讲解. 可持久化线段树指的是一种基于线段树的可回溯历史状态的数据结构.我们想要保 ...
- 洛谷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极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
随机推荐
- Python 垃圾回收机制(转)
概述 python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略. 引用计数 Python语言默认采用的垃圾收集机制是『引用计数法 Reference Counting』,该算法最早 ...
- oracle 单实例DG(闪回技术四)
一,flashback Oracle Flashback技术是一组数据库特性,它可以让你查看数据库对象的过去状态,或者将数据库对象返回到以前的状态,而无需使用基于时间点的介质恢复.根据数据库的变化,闪 ...
- Zookeeper概念学习系列之zookeeper是什么?
1. Zookeeper是Hadoop的分布式协调服务. 2. 分布式应用程序可以基于它,来实现同步服务,配置维护和命名服务等. 3. zookeeper可以保证数据在zookeeper集群之间的数据 ...
- rpm重装python和yum
前些天升级的python, yum就不能用了, 提示 "No module named yum", 然后搜索了一下, 说要重装python和yum, 也没多想, 就按照那些教程去做 ...
- s中的闭包
今天看了关于js闭包方面的文章,还是有些云里雾里,对于一个菜鸟来说,学习闭包确实有一定的难度,不说别的,能够在网上找到一篇优秀的是那样的不易. 当然之所以闭包难理解,个人觉得是基础知识掌握的不牢,因为 ...
- [转]Implementing User Authentication in ASP.NET MVC 6
本文转自:http://www.dotnetcurry.com/aspnet-mvc/1229/user-authentication-aspnet-mvc-6-identity In this ar ...
- 关于微信小程序的动态跳转
最近在研究微信小程序.在做一个简单的购物小程序时,遇到一个问题:如何通过扫码实现动态的跳转页面功能, 通过研究终于找到了解决方法: 首先当然要实现扫码解析功能js的代码: click: functio ...
- inteliJ IDEA使用SVN进行代码管理
inteliJ 自带版本控制,所以不用像网上其他人说的那样,装第三方插件. 首先装完inteliJ 后,在File-->Setting-->Version Control中选择Subver ...
- Protocol Buffers序列化原理
1. 使用Varint编码数据,越小的数据,用少的字节编码.如小于128的用一个字节编码,大于128的用多个字节编码.同时,每个字节最高为1或者0表示是否为数字的一部分. 2. 由于负数的补码表示很大 ...
- 2.storm的安装
1.前提是linux系统已经安装了上一篇讲的Zookeeper和jdk[1.7及以上版本]还有python[centos已经自带,2.6及以上版本] 2.解压storm压缩包 sudo tar -zx ...