洛谷P3834 【模板】可持久化线段树 2(主席树)
题目背景
这是个非常经典的主席树入门题——静态区间第 kk 小。
数据已经过加强,请使用主席树。同时请注意常数优化。
题目描述
如题,给定 nn 个整数构成的序列 aa,将对于指定的闭区间 [l,r][l,r] 查询其区间内的第 kk 小值。
输入格式
第一行包含两个整数,分别表示序列的长度 nn 和查询的个数 mm。
第二行包含 nn 个整数,第 ii 个整数表示序列的第 ii 个元素 aia**i。
接下来 mm 行每行包含三个整数 l,r,kl,r,k , 表示查询区间 [l,r][l,r] 内的第 kk 小值。
输出格式
对于每次询问,输出一行一个整数表示答案。
输入输出样例
输入 #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=5,数列长度为 55,数列从第一项开始依次为{25957,6405,15770,26287,26465}{25957,6405,15770,26287,26465}。
- 第一次查询为 [2,2][2,2] 区间内的第一小值,即为 64056405。
- 第二次查询为 [3,4][3,4] 区间内的第一小值,即为 1577015770。
- 第三次查询为 [4,5][4,5] 区间内的第一小值,即为 2628726287。
- 第四次查询为 [1,2][1,2] 区间内的第二小值,即为 2595725957。
- 第五次查询为 [4,4][4,4] 区间内的第一小值,即为 2628726287。
数据规模与约定
- 对于 20%20% 的数据,满足 1≤n,m≤101≤n,m≤10。
- 对于 50%50% 的数据,满足 1≤n,m≤1031≤n,m≤103。
- 对于 80%80% 的数据,满足 1≤n,m≤1051≤n,m≤105。
- 对于 100%100% 的数据,满足 1≤n,m≤2×1051≤n,m≤2×105,∣ai∣≤109∣a**i∣≤109,1≤l≤r≤n1≤l≤r≤n,1≤k≤r−l+11≤k≤r−l+1。
板题,因为求的是区间第k小,于是离散化后建主席树查询即可。
#include <bits/stdc++.h>
#define N 200005
using namespace std;
int n, q;
int cnt = 0;//总的节点数
int sum[N << 5], L[N << 5], R[N << 5];//主席树一定不能吝啬空间
int a[N], b[N];
int T[N];
int build(int l, int r) {
int rt = ++cnt;
sum[rt] = 0;
int mid = (l + r) >> 1;
if(l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
int modify(int pre, int l, int r, int x) {
int rt = ++cnt;
L[rt] = L[pre]; R[rt] = R[pre];
sum[rt] = sum[pre] + 1;
if(l < r) {
int mid = (l + r) >> 1;
if(x <= mid) L[rt] = modify(L[pre], l, mid, x);
//注意这里是x <= mid !!!而不是线段树常见写法
//因为这是对权值建立的线段树!
else R[rt] = modify(R[pre], mid + 1, r, x);
}
return rt;
}
int query(int u, int v, int l, int r, int k) {
if (l >= r) return l;
int x = sum[L[v]] - sum[L[u]];
int mid = (l + r) >> 1;
if (x >= k) return query(L[u], L[v], l, mid, k);
else return query(R[u], R[v], mid+1, r, k-x);
}
int main() {
cin >> n >> q;
vector<int> v;
for(int i = 1; i <= n; i++) {
cin >> a[i];
v.push_back(a[i]);
}
sort(v.begin(), v.end());
vector<int>::iterator it = unique(v.begin(), v.end());
v.erase(it, v.end());
T[0] = build(1, v.size());
for(int i = 1; i <= n; i++) {
int tmp = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
b[tmp] = a[i];
T[i] = modify(T[i - 1], 1, v.size(), tmp);
}
for(int i = 0; i < q; i++) {
int l, r, k;
cin >> l >> r >> k;
int ans = query(T[l - 1], T[r], 1, v.size(), k);
cout << b[ans] << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!