第k小的数
题目描述
你为SKZ公司的数据结构部门工作,你的工作是重新写一个程序,这个程序能快速地找到一段数列中第k小的数。
就是说,给定一个整数数列a[1..n],其中每个元素都不相同,你的程序要能回答一组格式为Q (i , j , k)的查询,Q(i, j ,k)的意思是“在a[i..j]中第k小的数是多少?”
例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),数列段a[2..5] = {5, 2, 6, 3},第3小的数是5,所以答案是5。
输入
第一行包括一个正整数n,代表数列的总长度,还有一个数m,代表有m个查询。n、m满足:1≤n≤100 000,1≤m≤5 000。
第二行有n个数,代表数列的元素,所有数都不相同,而且不会超过109。
接下来有m行,每行三个整数i、j、k,代表一次查询,i、j、k满足:1≤i≤j≤n, 1≤k≤j−i+1。
输出
每行一个输出每个查询的答案
样例输入
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
样例输出
5
6
3
主席树裸题,就是这样
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 150010
int lim=1000000000;
int cnt,n,m;
int root[N],w[N*31];
int ch[N*31][2];
void addnew(int x,int &y,int l,int r,int v)
{
y=++cnt;
w[y]=w[x]+1;
if(l==r) return;
ch[y][0]=ch[x][0],ch[y][1]=ch[x][1];
int mid=(l+r)>>1;
if(v<=mid) addnew(ch[x][0],ch[y][0],l,mid,v);
else addnew(ch[x][1],ch[y][1],mid+1,r,v);
}
int check(int L,int R,int k)
{
int x=root[L-1],y=root[R];
int l=-lim,r=lim;
while(l<r)
{
int mid=(l+r)>>1;
if(w[ch[y][0]]-w[ch[x][0]]<k)
{
l=mid+1;
k-=w[ch[y][0]]-w[ch[x][0]];
y=ch[y][1],x=ch[x][1];
}
else r=mid,x=ch[x][0],y=ch[y][0];
}
return l;
}
int main()
{
scanf("%d%d",&n,&m);
int aa,bb,cc;
for(int i=1;i<=n;i++)
{
scanf("%d",&aa);
addnew(root[i-1],root[i],-lim,lim,aa);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&aa,&bb,&cc);
printf("%d\n",check(aa,bb,cc));
}
}