博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
poj 2104 K-th Number 主席树+超级详细解释
阅读量:5812 次
发布时间:2019-06-18

本文共 2418 字,大约阅读时间需要 8 分钟。

poj 2104 K-th Number 主席树+超级详细解释

传送门:

题目大意:给出一段数列,让你求[L,R]区间内第几大的数字!

在这里先介绍一下主席树! 

如果想了解什么是主席树,就先要知道线段树,主席树就是n棵线段树,因为线段树只能维护最大值或者最小值,要想求出第二大的数字怎么办呢?两颗线段树呗!好,那么第n大呢,就可以构造n棵线段树,这样的内存是显然会爆掉的,那么怎么办呢?因为每一次更新都是更新的是从叶子节点到根节点的一条路,路的长度大约是logn,如下图红色的为更新的时候变化的节点: 
这里写图片描述

当进行更新操作的时候,也就是新建一个线段树,但是这可线段树不会全部都建立起来,只把修改的节点建立一下就可以,如果节点不修改就可以用以前线段树的节点指向当前新建的线段树的节点,先看下图然后在解释: 

这里写图片描述 
对于没有修改的节点就直接指向新建的线段树就可以了! 
对于每一棵线段树都是类似于下图的样子的: 
这里写图片描述 
这棵树表示数字1出现了三次,数字2出现了1次,数字三出现了一次,数字五出现了4次,数字6出现了三次; 
用一个数组来表示就是sum[i]=j表示数字i出现了j次。 
这样在通过一般线段树的pushdown操作就变为了我们熟悉的线段树了!如下图: 
这里写图片描述

下面据一个例子[1,1,2,1,5,5,5,5,6,6,6,3]解释一下建立线段树的全部过程: 

首先是一棵空的线段树 
这里写图片描述 
顺序的由第一个元素开始进入线段树并且开始更新: 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述

这样我们就可以知道不同状态下的线段树了,比如sum[i]当前节点对应的数字出现了几次,对于[L,R]这个区间我们就可以用sum[R]sum[L1]表示出如果这个区间大于k那么就从他的左子树接着找,否则从右子树中找K(sum[R]sum[L1])这个多个就是第k大的数字! 

这个基于的原理举个例子就知道了我们知道区间[1,3,3,4,5]区间一共有5个元素那么第5大的就是最后一个元素了!

AC代码

#include 
#include
#include
#include
#include
using namespace std; const int MAXN = 100010; const int N = MAXN*40; int n,m,q,tot; int T[MAXN],A[MAXN],t[MAXN]; int lson[N],rson[N],sum[N]; vector
V; int getid(int x) //离散化 { return lower_bound(V.begin(),V.end(),x)-V.begin()+1; } int build(int l,int r) //建立一棵空树 { int rt = tot++; sum[rt] = 0; if(l!=r){ int mid=(l+r)>>1; lson[rt] = build(l,mid); rson[rt] = build(mid+1,r); } return rt; } int update(int rt,int pos) //把数组中的元素一次加入新的线段树中 { int nrt = tot++; int tmp = nrt; sum[nrt] = sum[rt]+1; int l=1,r=m; while(l
>1; if(pos<=mid) { lson[nrt] = tot++; rson[nrt] = rson[rt]; nrt = lson[nrt]; rt = lson[rt]; r = mid; }else { rson[nrt] = tot++; lson[nrt] = lson[rt]; nrt = rson[nrt]; rt = rson[rt]; l=mid+1; } sum[nrt] = sum[rt]+1; } return tmp; } int query(int lrt,int rrt,int k) { int l=1,r=m; while(l
>1; int cnt = sum[lson[rrt]] - sum[lson[lrt]]; if(cnt>=k) { r = mid; lrt = lson[lrt]; rrt = lson[rrt]; } else { l = mid+1; k-=cnt; lrt = rson[lrt]; rrt = rson[rrt]; } } return l; } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&q);tot=0; for(int i=1;i<=n;i++) { scanf("%d",&A[i]); V.push_back(A[i]); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); m=V.size(); T[0] = build(1,m); for(int i=1;i<=n;i++) { T[i] = update(T[i-1],getid(A[i])); } while(q--) { int x,y,k; scanf("%d%d%d",&x,&y,&k); printf("%d\n",V[query(T[x-1],T[y],k)-1]); } return 0; }

 

转载于:https://www.cnblogs.com/Renyi-Fan/p/8117555.html

你可能感兴趣的文章
微服务架构会和分布式单体架构高度重合吗
查看>>
如何测试ASP.NET Core Web API
查看>>
《The Age of Surge》作者访谈
查看>>
测试人员的GitHub
查看>>
Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
查看>>
有关GitHub仓库分支的几个问题
查看>>
无服务器计算的黑暗面:程序移植没那么容易
查看>>
云原生的浪潮下,为什么运维人员适合学习Go语言?
查看>>
Webpack入门教程三十
查看>>
EAServer 6.1 .NET Client Support
查看>>
锐捷交换机密码恢复(1)
查看>>
Kali linux virtualbox rc=1908 错误解决办法
查看>>
linux软件包管理之三(源代码安装)
查看>>
数据库三范式是什么?
查看>>
[转载]设置Ubuntu自动连接无线,无须再输入密钥环和无线密码
查看>>
九叔Xen App测试报告
查看>>
Apache配置
查看>>
Ext gridPanel 单元格数据的渲染
查看>>
Android SDK 的下载代理
查看>>
Method Swizzling对Method的要求
查看>>