划分树:解决静态区间第K值问题
#include<bits/stdc++.h>
using namespace std;
const int N=200005,M=20;
int n,m;
#define mid ((le+ri)>>1)
#define lson le,mid,dep+1
#define rson mid+1,ri,dep+1
struct Node{
int num[N],toleft[N];
};
Node t[M];
int sorted[N];
void build(int le,int ri,int dep){
if(le==ri){ //如果达到叶节点,返回
return;
}
int key=sorted[mid]; //找到目前这个区间的中位数
int equ=mid-le+1; //中位数左边应该有多少个数
for(int i=le;i<=ri;i++){
if(t[dep].num[i]<key){
equ--;
//左边应该都是<=中位数的数,所以这样能找到需要往左边填几个等于中位数的数
}
}
int tl=0;//指已经进入左子区间多少个数了
int it1=le-1,it2=mid; //左右儿子指针
for(int i=le;i<=ri;i++){ //枚举区间内每个数
int now=t[dep].num[i];
if(now<key||(now==key&&equ)){ //如果这个数小于中位数||这个数等于中位数且左边区间没有被填满
if(now==key){
equ--; //等于的数填进去后,需要等于中位数的数填的数量--
}
tl++; //左边区间的数+1
t[dep+1].num[++it1]=now; //记得是下一层,下一层就是这个区间的左右区间
}
else{
t[dep+1].num[++it2]=now; //同上
}
t[dep].toleft[i]=tl; //记录这个数所在的区间内的数有多少去了他左儿子内及左儿子左边
}
build(lson);//向下递归
build(rson);
}
int query(int le,int ri,int dep,int x,int y,int z){
//区间左坐标,区间右坐标,深度,查询的区间左坐标,查询的区间右坐标,查询区间的第几大值
if(le==ri){ //如果到达叶节点,返回这个点的值
return t[dep].num[le];
}
int tl=0,del=t[dep].toleft[y];//这个区间的子区间内的数+左边的数
if(le!=x){
tl=t[dep].toleft[x-1];//减去之前的区间的数
del-=tl;
}
//此时del代表这个区间的子左区间的数
int nx,ny; //新区间左右坐标
if(del>=z){
//如果子左区间的数多于你要找的值那么就在左区间里
nx=le+tl;
ny=nx+del-1;
return query(lson,nx,ny,z);
}
else{
//否则在右区间里
nx=mid+1+x-tl-le;
ny=nx+y-x-del;
return query(rson,nx,ny,z-del);
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
t[0].num[i]=x;
sorted[i]=t[0].num[i];
}
sort(sorted+1,sorted+1+n);
build(1,n,0);
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
int ans=query(1,n,0,x,y,z);
printf("%d\n",ans);
}
return 0;
}