题目链接
这是个非常经典的主席树入门题——静态区间第 k 小。
数据已经过加强,请使用主席树。同时请注意常数优化。
如题,给定 n 个整数构成的序列 a,将对于指定的闭区间 [l, r] 查询其区间内的第 k 小值。
第一行包含两个整数,分别表示序列的长度 n 和查询的个数 m。
第二行包含 n 个整数,第 i 个整数表示序列的第 ii 个元素 a i a_i ai 。
接下来 m 行每行包含三个整数 l , r , k , l, r, k, l,r,k, 表示查询区间 [ l , r ] [l, r] [l,r] 内的第 k 小值。
对于每次询问,输出一行一个整数表示答案。
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
主席树模板题,AC代码如下:
#include
#include
#include
using namespace std;
const int N=2e5+5;
int n,m,siz,l,r,k,cnt=0;
int a[N];//原数组
int s[N];//去重后的数组
int rk[N];//记录原数组的元素的排名
int root[N];//记录每个根节点的编号
struct ptree{
int l,r,sum;//sum记录经过该节点的次数
}t[N*20];
void build(int &node,int l,int r){
node=++cnt;
if(l==r) return;
int mid=l+r>>1;
build(t[node].l,l,mid);
build(t[node].r,mid+1,r);
}
void update(int &node,int last,int l,int r,int s){
node=++cnt;
t[node]=t[last];
++t[node].sum;
if(l==r) return;
int mid=l+r>>1;
if(s<=mid) update(t[node].l,t[last].l,l,mid,s);
else update(t[node].r,t[last].r,mid+1,r,s);
}
int query(int node,int last,int l,int r,int k) {
if (l == r) return s[l];
int sum = t[t[node].l].sum - t[t[last].l].sum, mid = l + r >> 1;
if (k <= sum) return query(t[node].l, t[last].l, l, mid, k);
else return query(t[node].r, t[last].r, mid + 1, r, k - sum);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memcpy(s,a,sizeof(s));
sort(s+1,s+1+n);
siz=unique(s+1,s+1+n)-s-1;
build(root[0],1,siz);
for(int i=1;i<=n;i++) rk[i]=lower_bound(s+1,s+1+siz,a[i])-s;
for(int i=1;i<=n;i++) update(root[i],root[i-1],1,siz,rk[i]);
while(m--){
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",query(root[r],root[l-1],1,siz,k));
}
}