主席树学习笔记(静态区间第k大)
题目背景
这是个非常经典的主席树入门题——静态区间第K小
数据已经过加强,请使用主席树。同时请注意常数优化
题目描述
如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入输出格式
输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个整数,表示这个序列各项的数字。
接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值。
输出格式:
输出包含k行,每行1个整数,依次表示每一次查询的结果
输入输出样例
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
6405
15770
26287
25957
26287
题面非常清晰,区间第k大。首先,每次排序再还原肯定是要跪的。
这时主席树就出现了。
主席树,倒不如说是榕树(有多个根节点,但是却不是森林)。可以说是在用空间换时间。
其实这题,主席树的主体应该是一棵权值线段树。
别人都说主席树是在每个位置维护了一颗线段树,我一直以为是每一个叶节点,其实是在每一个...好吧就是叶节点。
这里可能要把历史值那一题拿出来讲讲,主席树的每一次修改只修改一条链,而不是整个换血,所以为我们的查询提供了方便。
模拟一发:
7 1
1 5 2 6 3 7 4
建树:(感谢@bestFy的数据和图!)
插完所有的之后:
(妥妥的权值线段树)
最终图只是最后一棵线段树的样子,之前的各个线段树依旧在保存着。
于是就出现了旧节点和新建的节点公用一个根节点的情况。于是就可以愉快地进行差分了(因为两个新旧节点是等价的啊)
∴两棵线段树的对应节点相减就是对应区间有的数字啦
所以对于一个区间[l, r],我们可以每次算出在[l, mid]范围内的数,如果数量>=k(k就是第k大),就往左子树走,否则就往右子树走。
于是区间第k大就完成了。
贴代码:
#include<bits/stdc++.h>
变量解释:sum:节点的总数
rt:新旧树的根节点
ls:左儿子
rs:右儿子
tot:当前节点编号
using namespace std;
const int maxn=;
int n,m,tot,sum[maxn<<],rt[maxn<<],ls[maxn<<],rs[maxn<<],a[maxn],b[maxn],len; int build(int l,int r)
{
int root=++tot;//每新建一个节点(根)
if(l==r) return root;//如果到叶节点了返回根的值,我们要记录下来
int mid=l+r>>;//二分区间
ls[root]=build(l,mid);//记录下一层的根,也就是左儿子
rs[root]=build(mid+,r);//同上
return root;//返回大根
}
int updata(int l,int r,int root,int k)
{
int newroot=++tot;//新建的根的编号
ls[newroot]=ls[root];//记录
rs[newroot]=rs[root];//建一个等价的节点,左右儿子也是旧根节点的左右儿子
sum[newroot]=sum[root]+;//每新建一条链增加一个有值的点,所以+1(权值线段树)
if(l==r) return newroot;
int mid=l+r>>;
if(k<=mid) ls[newroot]=updata(l,mid,ls[newroot],k);//
else rs[newroot]=updata(mid+,r,rs[newroot],k);
return newroot;
}
int query(int fl,int fr,int l,int r,int k)
{
int mid=l+r>>,x=sum[ls[fr]]-sum[ls[fl]];//查询区间里的东西,先遍历左儿子,不行再往右跑
if(l==r) return l;
else if(k<=x) return query(ls[fl],ls[fr],l,mid,k);//差分了
else return query(rs[fl],rs[fr],mid+,r,k-x);//差分了
}
int main()
{
int i,x,l,r,k;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+,b++n);
len=unique(b+,b++n)-b-;//去重,神仙操作
//printf("%d\n",len);
rt[]=build(,len);//根的“tot”(下标,第几个点)
for(i=;i<=n;i++)//要对每一个节点建树,所以n次加边
{
x=lower_bound(b+,b++len,a[i])-b;//离散化,更新相对大小,也就是把所有数的编号弄小
rt[i]=updata(,len,rt[i-],x);//新根的编号
}
for(i=;i<=m;i++)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[query(rt[l-],rt[r],,len,k)]);//query返回的是下标,所以要这么长一串。。
}
for(int i=;i<=*n;i++)
{
//if(sum[i]==0)break;
printf("%d ",sum[i]);
}
return ;
}
(完)
主席树学习笔记(静态区间第k大)的更多相关文章
- ZOJ -2112 Dynamic Rankings 主席树 待修改的区间第K大
Dynamic Rankings 带修改的区间第K大其实就是先和静态区间第K大的操作一样.先建立一颗主席树, 然后再在树状数组的每一个节点开线段树(其实也是主席树,共用节点), 每次修改的时候都按照树 ...
- 线段树专题2-(加强版线段树-可持续化线段树)主席树 orz! ------用于解决区间第k大的问题----xdoj-1216
poj-2104(区间第K大问题) #include <iostream> #include <algorithm> #include <cstdio> #incl ...
- 可持久化线段树(主席树)——静态区间第k大
主席树基本操作:静态区间第k大 #include<bits/stdc++.h> using namespace std; typedef long long LL; ,MAXN=2e5+, ...
- HDU3473--Minimum Sum(静态区间第k大)
Minimum Sum Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ...
- poj2104&&poj2761 (主席树&&划分树)主席树静态区间第k大模板
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 43315 Accepted: 14296 Ca ...
- 主席树(静态区间第k大)
前言 如果要求一些数中的第k大值,怎么做? 可以先就这些数离散化,用线段树记录每个数字出现了多少次. ... 那么考虑用类似的方法来求静态区间第k大. 原理 假设现在要有一些数 我们可以对于每个数都建 ...
- 静态区间第k大(归并树)
POJ 2104为例 思想: 利用归并排序的思想: 建树过程和归并排序类似,每个数列都是子树序列的合并与排序. 查询过程,如果所查询区间完全包含在当前区间中,则直接返回当前区间内小于所求数的元素个数, ...
- 主席树初步学习笔记(可持久化数组?静态区间第k大?)
我接触 OI也快1年了,然而只写了3篇博客...(而且还是从DP跳到了主席树),不知道我这个机房吊车尾什么时候才能摸到大佬们的脚后跟orz... 前言:主席树这个东西,可以说是一种非常畸形的数据结构( ...
- HDU 2665 Kth number(主席树静态区间第K大)题解
题意:问你区间第k大是谁 思路:主席树就是可持久化线段树,他是由多个历史版本的权值线段树(不是普通线段树)组成的. 具体可以看q学姐的B站视频 代码: #include<cmath> #i ...
随机推荐
- 从零开始的vue学习笔记(七)
前言 今天花一天时间阅读完vuex的官方文档,简单的做一下总结和记录 Vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,以前的符合"单向数据流"理念的 ...
- 点集配准技术(ICP、RPM、KC、CPD)
在计算机视觉和模式识别中,点集配准技术是查找将两个点集对齐的空间变换过程.寻找这种变换的目的主要包括:1.将多个数据集合并为一个全局统一的模型:2.将未知的数据集映射到已知的数据集上以识别其特征或估计 ...
- 两种高效的事件处理模型:Reactor模式和Proactor模式
随着IO多路复用技术的出现,出现了很多事件处理模式.同步I/O模型通常由Reactor模式实现,而异步I/O模型则由Proactor模式实现. Reactor模式: Reator类图如上所示,Reac ...
- 寻找子串位置<codevs>
KMP板子题; 如果不会可以参考其他算法书 代码: #include<iostream> #include<stdio.h> #include<stdlib.h> ...
- 编译 lame for iOS
网上找了许多编译lame的教程,结果都是编译失败,多次尝试后发现是编译脚本放错路径了,记录下编译的过程,把编译脚本放到源码文件夹中和修改编译脚本中的目录是关键: 一.首先去Lame官网 http:// ...
- Jenkins源代码管理(SVN)
Subversion 安装插件 1.首先将本地的自动化用例打包上传svn 2.配置jenkins源代码管理(每次执行jenkins时,会自动check-out配置地址中的代码到Jenkins的工作空间 ...
- Jackson中@JsonProperty等常用注解
Java生态圈中有很多处理JSON和XML格式化的类库,Jackson是其中比较著名的一个.虽然JDK自带了XML处理类库,但是相对来说比较低级 本文将介绍的Jackson常用注解:精简概述 Jack ...
- windows显示文件后缀名
win+E 进入到计算机 点击组织 点击文件夹和搜索选项 先点击查看,然后去掉勾选隐藏已知文件类型的扩展名
- 用JavaScript制作banner轮播图
JavaScript_banner轮播图 让我们一起来学习一下用js怎么实现banner轮播图呢? 直接看代码: <!DOCTYPE html> <html> <head ...
- C# 控件 RichTextBox 显示行号,并且与Panel相互联动
我们在使用到WINFORM窗体工作中,要求RichTextBox 加入行号: 之前有看到大牛们写的,但是太复杂繁多,而且有用双TextBox进行联动,非常不错,今天我们尝试RichTextBox +P ...