前言:
最近心(po)血(yu)来(ya)潮(li)学习了一下主席树。(再不学就落伍了)
主席树,即可持久化线段树,支持维护和查询区间的第\(k\)大(小)、区间不同种类个数等,基于线段树的思想之上

结构分析

主席树会维护\([1,n]\)中点的个数(可以理解为,一颗弹珠从树根放下,滑到叶节点时,走过的路径都\(+1\))
现在假设有一组数 \(\{a_1,a_2,...,a_7\}\),离散化后编号为 \(\{1,2,...,7\}\),现在以\(\{1,7,6,2,3,5,4\}\)的顺序进入主席树
下面来演示一下这个过程(PowerPoint冠名)








下面来张动图(PowerPoint冠名)

耐心等一会,总会等到GIF开头的(该动图时长32秒)

那么通过这种结构该如何寻找区间第\(k\)大(小)值呢?


算法实现

还是原来的数组 \(\{1,7,6,2,3,5,4\}\),现在来举个栗子:求\([2,5](\{7,6,2,3\})\)这一段的第\(2\)小值(\(3\))
首先要了解这样一个概念:求区间第\(k\)小值,相当于在区间内找一个值,使有\(k\)个数小于等于这个值(废话)

不(hen)难发现,在主席树里,左子树的值一定小于右子树的值。那么,只要判断左子树里的数的个数是否\(\ge2\),如果是,那么区间第\(2\)小值一定在左子树里,反之在右子树里
那么能否建\(N\)棵线段树呢?    :小朋友,我来了!
通过认真的观察,发现——


这两张图中,真正修改了值的节点并不多。这时,两棵线段树完全可以使用共用的节点,这样就可以大大的降低空间复杂度。那么,该如何处理\([l,r]\)中比当前值小的数的个数呢?
这里就要用到二分和前缀和的思想了。举个栗子,如果\([1,l-1]\)中有\(sum_1\)个数比当前值小,\([1,r]\)中有\(sum_2\)个数比当前值小,那么\([l,r]\)中就有 \(sum_2-sum_1\)个值比当前值小
回到刚才的问题:

数组 \(\{1,7,6,2,3,5,4\}\),求\([2,5](\{7,6,2,3\})\)这一段的第\(2\)小值(\(3\))

我们挑出第\((2-1)\)次插入时的树和第\(5\)次插入时的树,经过观察——

你发现了什么?
举个栗子,我们看上一棵树中的代表\([5,7]\)的节点和下一棵树的对应节点,可以发现:它们的差就是\(\{7,6,2,3\}\)中在\([5,7]\)上的数的个数。那么,上一棵树中的代表\([1,4]\)的节点和下一棵树的对应节点之差为\(2\),这说明第\(2\)小值必定在\([1,4]\)区间内,由此即可求出第\(2\)小值——\(3\)


代码实现

\(L,R\) 表示左右子树,\(sum\) 就是图中所维护的维护\([1,n]\)中点的个数,\(rank\) 为离散化数组,\(root\) 表示这么多线段树的根。答案递归求解即可。( \(fm(x)\) 表示初始化 \(x\) )

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define f(x,y) for(register int i=x;i<=y;i++)
#define G ch=getchar()
#define rd int
#define in(x) x=read();
#define node int
#define mid ((l+r)>>1)
#define fm(x) memset(x,0,sizeof(x))
#define maxN 200000
using namespace std;
inline rd read();
class president_tree
{public:node query(int x,int y,int w) {return rank[_query(root[x-1],root[y],1,_size,w)];}void build(int a[],int n){tot=0; fm(rank); fm(root); fm(sum); fm(L); fm(R);for(register int i=1;i<=n;i++) rank[i]=a[i];sort(rank+1,rank+n+1);_size=unique(rank+1,rank+n+1)-rank-1;root[0]=_build(1,_size);for(register int i=1;i<=n;i++)root[i]=update(root[i-1],1,_size,lower_bound(rank+1,rank+_size+1,a[i])-rank);}private:int tot,_size;node rank[maxN+1],root[maxN+1],sum[(maxN<<5)+1],L[(maxN<<5)+1],R[(maxN<<5)+1];;node _build(int l,int r){int k=++tot; sum[k]=0;if(l<r){L[k]=_build(l,mid);R[k]=_build(mid+1,r);}return k;}node update(int pre,int l,int r,int w){int k=++tot; L[k]=L[pre]; R[k]=R[pre]; sum[k]=sum[pre]+1;if(l<r)if(w<=mid) L[k]=update(L[pre],l,mid,w);else R[k]=update(R[pre],mid+1,r,w);return k;}node _query(int u,int v,int l,int r,int k){if(l>=r) return l;int w=sum[L[v]]-sum[L[u]];if(w>=k) return _query(L[u],L[v],l,mid,k);else return _query(R[u],R[v],mid+1,r,k-w);}
};
int n,m,a[maxN+1],b[maxN+1],x,y,w;
president_tree tree;
int main()
{in(n); in(m); f(1,n) in(a[i]);tree.build(a,n);f(1,m){in(x); in(y); in(w);printf("%d\n",tree.query(x,y,w));}return 0;
}
inline rd read()
{char ch=getchar();rd num=0,f=1;while((ch<'0' || ch>'9') && ch!='-') G;if(ch=='-') {f=-1; G;}while(ch>='0' && ch<='9') {num=num*10+ch-'0'; G;}return num*f;
}

写在最后

对主席树的认识与理解,到这篇博客的诞生,是起源于这两位巨佬的博客的——
最详细的讲解,让你一次学会主席树
【Notes】【主席树】hdu2665 Kth number
在此表示衷心的感谢!
(我才不会告诉你这篇博客写到一半未保存,网页就莫名其妙关闭了呢 QWQ)

转载于:https://www.cnblogs.com/ezsyshx/p/10403244.html

可持久化线段树——主席树相关推荐

  1. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

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

    我似乎很少写这种算法博客 可持久化线段树概念 概念介绍(类比帮助理解) 简单分析一下时间和空间复杂度(内容池) 模板 结构体变量 建树模板 单点修改模板 单点查询模板 区间修改模板(pushup) 区 ...

  3. 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)...

    J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...

  4. 模板三连击:树状数组+线段树+主席树

    没事儿干,复习模板...... 1.树状数组 本来不想写这个的,但是反正就几分钟就打完了,所以就写了,水AC数. 洛谷 P3374 [模板]树状数组 1 1 #include<cstdio> ...

  5. 2020-2021年度第二届全国大学生算法设计与编程挑战赛 (春季赛)- 天才的操作(线段树+主席树+树上倍增)

    题目链接:点击查看 题目分析:刚看到这个题目的时候,口胡了一个假算法,觉得对于每次询问的操作 [l,r][l,r][l,r] ,只需要找到指令集区间 [l,r][l,r][l,r] 内覆盖到点 kkk ...

  6. Governing sand(权值线段树/主席树)

    链接:https://ac.nowcoder.com/acm/contest/887/C 来源:牛客网 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 65536K,其他语言13107 ...

  7. UOJ#218. 【UNR #1】火车管理 线段树 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...

  8. [联合集训6-25] 蓝雨 线段树+主席树+hash

    先考虑p=qp=qp=q的情况,习惯先把求第kkk大变成求第k" role="presentation" style="position: relative;& ...

  9. 洛谷 - P4755 Beautiful Pair(笛卡尔树+主席树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a,现在一个数对 ( i , j ) 如果满足 a[ i ] * a[ j ] <=max( a[ i ] ~ a[ j ] ),则称其 ...

最新文章

  1. python 之三级菜单
  2. H3核心板开发笔记(一):编译及烧写方式
  3. elasticsearch5.4.3、kibana分布式安装
  4. jstl之c:forEach的使用
  5. ASP.NET三层架构的优点和缺点
  6. 科学家预测:未来100万年人类将变成半机械人类
  7. Git(6):使用git命令将本地项目提交至远程仓库
  8. python简单实战项目:《冰与火之歌1-5》角色关系图谱构建——人物关系可视化
  9. 基于扩频信号的水声信道数据传输系统仿真,研究满足了WSSUS假设的瑞利信道模型,采用相干BPSK调制,联合多普勒Rake接收机
  10. 目前流行的、强大的基于Java的机器学习开发库精选
  11. SOSO发布国内首家高清街景地图 引领地图换代
  12. supermap地图展示OD图
  13. 重庆邮电大学 计算机学院导师,宋秀丽_重庆邮电大学研究生导师信息
  14. WH-1000XM3蓝牙连接笔记本电脑
  15. Spring Boot多数据源配置并通过注解实现动态切换数据源
  16. ESP32数据存储 nvs
  17. Innovus和ICC2中设置partial placement blockage的命令和区别
  18. 鸡尾酒问题,最小二乘法和范数的思考
  19. Python系列 49 内置模块:subprocess
  20. 用电脑搭建视频会议系统的方法

热门文章

  1. 谁说只有VGG才能做风格迁移,ResNet也可以
  2. 机器学习实战:k-近邻算法(手写数字识别)
  3. go二维map_mirrorlang——从0设计二维内存寻址语言及vm(五.内存管理的思考)
  4. idea使用ant配置_Linux下Jenkins+ant+jmeter[上]
  5. linux一键重装系统脚本,一键重装CentOS纯净版系统shell脚本
  6. python右对齐函数_python右对齐的实例方法
  7. navicat 批量修改列数据
  8. linux系统在硬盘上安装程序,在硬盘中安装Linux操作系统最简单的方法
  9. java序列化的作用
  10. Android Https相关完全解析 当OkHttp遇到Https