对于查询(i,j,k),输出 a_i........a_j 中按照升序排列中的第k个数。
使用线段树解决,节点维护的是去见排序的结果,然后用二分法找到第k个数。
书中示例程序数组地址有错误,在下面的程序中已经改正。
题源来自《挑战程序竞赛》第二版185页。
//
// 185_ k-th number.cpp
// changlle
//
// Created by user on 1/20/16.
// Copyright (c) 2016 user. All rights reserved.
//
#include <iostream>
#include <vector>
using namespace std;
const int ST_SIZE=(1<<18)-1;
const int MAX_N=100;
int N=7, M=3;
int A[7]={1,5,2,6,3,7,4};
int I[3]={2,4,1}, J[3]={5,4,7}, K[3]={3,1,3};
int nums[MAX_N]; //对A进行排序
vector<int> dat[ST_SIZE]; //线段树
void init (int k, int l, int r) {
if (r-l==1) { //左右正好能接上
dat[k].push_back(A[l]);
}
else {
int lch=k*2+1,rch=k*2+2;
init(lch,l,(l+r)/2);
init(rch,(l+r)/2,r);
dat[k].resize(r-l);
merge(dat[lch].begin(), dat[lch].end(), dat[rch].begin(),dat[rch].end(), dat[k].begin());//merge 函数 stl
}
}
int query (int i, int j, int x, int k, int l, int r) {
if(j<=l || r<=i) {
return 0;
}
if (i<=l && r <=j) {
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() {
for (int i=0;i<N;i++) nums[i]=A[i];
sort(nums,nums+N);
init(0,0,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=(ub+lb)/2;
int c=query(l,r,nums[md],0,0,N);
if (c>=k)
ub=md;
else
lb=md;
}
cout<<nums[ub]<<endl;
}
}
int main() {
solve();
return 0;
}