求区间第k大元素的值,
看代码的注释。
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define M 100001
#define md(x,y) (((x)+(y))>>1)
int sorted[M];
struct node{
int val[M]; //保存的是当前层的各个位上的数值
int num[M]; //记录比当前元素小的元素的和
double sum[M]; //记录当前元素之前进入左子树的元素和
}t[20]; //树的深度
void build(int lft,int rht,int p)
{
if(lft==rht) return;
int i,mid=md(lft,rht);
int isame=mid-lft+1,same=0;
//isame 记录分到左孩子的个数
for(i=lft;i<=rht;i++)
if(t[p].val[i]<sorted[mid]) isame--;
//先算下等于中间值的有几个
int ln=lft,rn=mid+1;
for(i=lft;i<=rht;i++){
if(i==lft){
t[p].num[i]=0;
t[p].sum[i]=0;
}else{
t[p].num[i]=t[p].num[i-1];
t[p].sum[i]=t[p].sum[i-1];
}
//如果大于,肯定进入右孩子,小于一定进入左孩子。
if(t[p].val[i]<sorted[mid]){
t[p].num[i]++;
t[p].sum[i]+=t[p].val[i];
t[p+1].val[ln++]=t[p].val[i];
}else if(t[p].val[i]>sorted[mid]){
t[p+1].val[rn++]=t[p].val[i];
}else{
//处理相等的时候的情况
if(same<isame){
same++;
t[p].num[i]++;
t[p].sum[i]+=t[p].val[i];
t[p+1].val[ln++]=t[p].val[i];
}
else{
t[p+1].val[rn++]=t[p].val[i];
}
}
}
build(lft,mid,p+1);
build(mid+1,rht,p+1);
}
double sum=0;
int query(int a,int b,int k,int p,int lft,int rht)
{
if(lft==rht) return t[p].val[a];
int s,ss,bb,b2; //s记录[a,b]进入做孩子的元素个数,ss记录[lft,a-1)中计入左孩子的元素的个数。sss记录区间[a,b]中小于第k大元素的和.b2表示[lft,a-1]中分到右孩子的个数,bb表示[a,b]中分到右孩子的个数
double sss;
int mid=md(lft,rht);
if(a==lft){
s=t[p].num[b];
ss=0;
sss=t[p].sum[b];
}else{
s=t[p].num[b]- t[p].num[a-1];
ss=t[p].num[a-1];
sss=t[p].sum[b]-t[p].sum[a-1];
}
if(s>=k){
a= lft+ss;
b= lft+ss+s-1;
return query(a,b,k,p+1,lft,mid);
}else{
bb=a-lft-ss;
b2=b-a-s;
a=mid+bb+1;
b=mid+1+bb+b2;
sum+=sss;
return query(a,b,k-s,p+1,mid+1,rht); //在右孩子中
}
}
int main()
{
int w,i;
scanf("%d",&w);
while(w--){
int n,k;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++){
scanf("%d",&t[0].val[i]);
sorted[i]=t[0].val[i];
}
sort(sorted+1,sorted+1+n);
build(1,n,0);
for(i=1;i<=k;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d\n",query(a,b,c,0,1,n));
}
}
}