洛谷 P3834 【模板】可持久化线段树 2(主席树)
创始人
2024-01-21 18:33:22
0

【模板】可持久化线段树 2

题目背景

这是个非常经典的可持久化权值线段树入门题——静态区间第 kkk 小。

数据已经过加强,请使用可持久化权值线段树。同时请注意常数优化

题目描述

如题,给定 nnn 个整数构成的序列 aaa,将对于指定的闭区间 [l,r][l, r][l,r] 查询其区间内的第 kkk 小值。

输入格式

第一行包含两个整数,分别表示序列的长度 nnn 和查询的个数 mmm。
第二行包含 nnn 个整数,第 iii 个整数表示序列的第 iii 个元素 aia_iai​。
接下来 mmm 行每行包含三个整数 $ l, r, k$ , 表示查询区间 [l,r][l, r][l,r] 内的第 kkk 小值。

输出格式

对于每次询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

样例输出 #1

6405
15770
26287
25957
26287

提示

样例 1 解释

n=5n=5n=5,数列长度为 555,数列从第一项开始依次为{25957,6405,15770,26287,26465}\{25957, 6405, 15770, 26287, 26465\}{25957,6405,15770,26287,26465}。

  • 第一次查询为 [2,2][2, 2][2,2] 区间内的第一小值,即为 640564056405。
  • 第二次查询为 [3,4][3, 4][3,4] 区间内的第一小值,即为 157701577015770。
  • 第三次查询为 [4,5][4, 5][4,5] 区间内的第一小值,即为 262872628726287。
  • 第四次查询为 [1,2][1, 2][1,2] 区间内的第二小值,即为 259572595725957。
  • 第五次查询为 [4,4][4, 4][4,4] 区间内的第一小值,即为 262872628726287。

数据规模与约定

  • 对于 20%20\%20% 的数据,满足 1≤n,m≤101 \leq n,m \leq 101≤n,m≤10。

  • 对于 50%50\%50% 的数据,满足 1≤n,m≤1031 \leq n,m \leq 10^31≤n,m≤103。

  • 对于 80%80\%80% 的数据,满足 1≤n,m≤1051 \leq n,m \leq 10^51≤n,m≤105。

  • 对于 100%100\%100% 的数据,满足 1≤n,m≤2×1051 \leq n,m \leq 2\times 10^51≤n,m≤2×105,∣ai∣≤109|a_i| \leq 10^9∣ai​∣≤109,1≤l≤r≤n1 \leq l \leq r \leq n1≤l≤r≤n,1≤k≤r−l+11 \leq k \leq r - l + 11≤k≤r−l+1。

    1、 值域线段树,可持久化线段树。
    cnt[x] 表示 x出现的次数
    2、主席树, 就是n颗线段树叠加。 每新扫描一个数,就增加
    log(n) + 1 个节点。 这些新节点,和原来的某些节点,构成这颗新的线段树。
    3、 求区间 [l, r] 第k小; 利用前缀和。
    4、主席树,插入,查询都需要双指针同步搜索

    5、离散化

#include 
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n, m;
vector v;struct node
{int ch[2];int s;	// 节点值域中有多少个数
}tr[N * 22];int root[N], idx; // root[k] 表示第k个数(第k个版本)树的根节点 void build(int &x, int l, int r)	//建一颗空树
{x = ++idx;if(l == r)return;int m = (l + r) >> 1;build(lc(x), l, m);build(rc(x), m + 1, r);
}void insert(int x, int& y, int l, int r, int v)	// x是前一版本的节点指针,y是当前版本的
{y = ++idx; tr[y] = tr[x]; tr[y].s++;if(l == r)return;int m = (l + r) >> 1;	//双指针同步搜索if(v <= m)insert(lc(x), lc(y), l, m, v);elseinsert(rc(x), rc(y), m + 1, r, v);
}//在主席树上查询区间 [l, r] 的第k小
//x是前一版本的指针,y是当前版本的指针
int query(int x, int y, int l, int r, int k)
{if(l == r)return l;int m = (l + r) >> 1;int s = tr[lc(y)].s - tr[lc(x)].s;	//这一步的意义, sum[r] - sum[l - 1]//比如求区间 [L, R]的第k小。 那么,第R颗线段树表示 前R个数出现次数的情况//那么,第L - 1颗线段树表示 前L - 1个数出现次数的情况//s = tr[lc(y)].s - tr[lc(x)].s, 就表示 区间 [L, R] 一共多少个数了if(k <= s)return query(lc(x), lc(y), l, m, k);elsereturn query(rc(x), rc(y), m + 1, r, k - s);
}void discrete()
{sort(v.begin(), v.end());v.erase(unique(v.begin(), v.end()), v.end());
}int getid(int x)	// 返回x的下标
{return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++i){scanf("%d", &a[i]);v.push_back(a[i]);}discrete();build(root[0], 1, n);for(int i = 1; i <= n; ++i){insert(root[i - 1], root[i], 1, n, getid(a[i]));}int l, r, k;while(m--){scanf("%d%d%d", &l, &r, &k);int id = query(root[l - 1], root[r], 1, n, k)  - 1;printf("%d\n", v[id]);}return 0;
}

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...