题目描述
如题,给定 n 个整数构成的序列 a,将对于指定的闭区间 [l,r] 查询其区间内的第 k 小值。
输入格式
第一行包含两个整数,分别表示序列的长度 n 和查询的个数 m。
第二行包含 n 个整数,第 i 个整数表示序列的第 i 个元素 ai。
接下来 m 行每行包含三个整数 l,r,k , 表示查询区间[l,r] 内的第 k 小值。
输出格式
对于每次询问,输出一行一个整数表示答案。
输入输出样例
输入 #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
解析:
我们前一篇写了可持续化线段树,我们只需对其进行修改一下即可。
P3919 【模板】可持久化线段树 1(可持久化数组)-CSDN博客
1.对数据进行离散化。
2.记录每个历史版本区间节点的个数。判断是否 进入左区间还是右区间。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define mid ((l+r)>>1)
int n,m,a[N],b[N];
int root[N],tot; //根节点,节点个数
int ls[N*20],rs[N*20],sum[N*20];
//sum:区间数的出现次数之和
void build(int &u,int l,int r)
{
u = ++tot;
if(l == r){
return;
}
build(ls[u],l,mid);
build(rs[u],mid+1,r);
}
void change(int &u,int v,int l,int r,int p) //建立每个版本的 根节点 和 左右儿子
{
u = ++tot;
ls[u] = ls[v];
rs[u] = rs[v];
sum[u] = sum[v]+1;
if(l== r) return;
if(p<= mid) change(ls[u],ls[v],l,mid,p);
else change(rs[u],rs[v],mid+1,r,p);
}
int query(int u,int v,int l,int r,int k)
{
if(l== r) return l;
int s = sum[ls[u]] - sum[ls[v]];//先看左端点的差值
if(k <= s) return query(ls[u],ls[v],l,mid,k);
else return query(rs[u],rs[v],mid+1,r,k-s);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b+1,b+n+1);
int bn = unique(b+1,b+n+1) - b-1;
for(int i = 1;i <= n;i++)
{
int p = lower_bound(b+1,b+bn+1,a[i]) - b;
change(root[i],root[i-1],1,bn,p);
}
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int p = query(root[r],root[l-1],1,bn,k);
printf("%d\n",b[p]);
}
return 0;
}
时间复杂度为:O(n*longn)