给定一个数列 a1,a2,a3......an和m个三元组表示查询,对于每个查询(i,j,k)输出 ai.....aj的升序排列中的第k个数。
我们把数列用线段树维护起来,线段树的每个节点维护了对应区间排好序的结果,计算在某个区间不超过x的数的个数,只要递归进行操作即可。
求出在某个区间里不超过x的数的个数之后,通过对x进行二分搜索来求出第k个数。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int SIZE=1<<18-1;
int A[]={1,5,2,6,3,7,4},M=3,N;
int I[]={2,4,1};
int J[]={5,4,7};
int K[]={3,1,3};
vector<int> dat[SIZE];
//构建线段树,k是节点编号,和区间[l,r)对应
void init(int k,int l,int r){
if(r-l==1){
dat[k].push_back(A[l]);
return ;
}
int lch=2*k+1,rch=2*k+2;
init(lch,l,(l+r)/2);
init(rch,(l+r)/2,r);
dat[k].resize(dat[lch].size()+dat[rch].size());
//合并两个儿子数列
merge(dat[lch].begin(),dat[lch].end(),dat[rch].begin(),dat[rch].end(),dat[k].begin());
}
//计算[i,j)区间中不超过x的数的个数,k是节点编号和区间[l,r)对应
int query(int i,int j,int x,int k,int l,int r){
if(j<=l || r<=i){
return 0; //完全不相交
}
else if(i<=l && j>=r){ //完全包含
return upper_bound(dat[k].begin(),dat[k].end(),x)-dat[k].begin();
}
else { //对儿子递归计算
int lc=query(i,j,x,k*2+1,l,(l+r)/2);
int rc=query(i,j,x,k*2+2,(l+r)/2,r);
return lc+rc;
}
}
void solve(){
sort(A,A+N);
for(int i=0;i<M;i++){
int l=I[i]-1,r=J[i],k=K[i];
int lb=-1,ub=N-1;
while(ub-lb>1){
int md=(lb+ub)/2;
int c=query(l,r,A[md],0,0,N);
if(c>=k) ub=md;
else lb=md;
}
cout<<A[ub]<<endl;
}
}
int main()
{
N=7;
init(0,0,N);
solve();
return 0;
}