求区间第K大数。
线段树每个区间维护一个有序的数组。
然后求解时二分第K大的数是多少。假设是t
再在线段树的每个区间里二分查询有多少小于等于t的,最后返回要查的区间里共有多少小于等于t的,如果小于K那么答案不可行,反之可行,每次查询是lognlogn的。
在建树时对于merge操作,线段树的每一层刚好要遍历n个元素,共logn层,所以总的时间复杂度仍是nlogn的
还是要学一下求第K大数正规做法:划分树
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100010
vector <int> G[maxn<<2];
int N;
int a[100010];
void pushup(int rt){
merge(G[rt<<1].begin(),G[rt<<1].end(),G[rt<<1|1].begin(),G[rt<<1|1].end(),G[rt].begin());
}
void build(int l,int r,int rt){
if(l==r){
G[rt].push_back(a[l]);
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
G[rt].resize(r-l+1);
pushup(rt);
}
int query(int L,int R,int k,int l,int r,int rt){
if(L<=l&&R>=r){
return upper_bound(G[rt].begin(),G[rt].end(),k)-G[rt].begin();
}
int m=(l+r)/2;
int res=0;
if(m>=L) res+=query(L,R,k,lson);
if(m<R) res+=query(L,R,k,rson);
return res;
}
int bsearch(int L,int R,int k){
int l=-1e9-1,r=1e9+1;
int num=0;
while(l+1<r){
int m=(l+r)>>1;
num=query(L,R,m,1,N,1);
if(num<k) l=m;
else r=m;
}
return r;
}
int main(){
int M;
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++){
scanf("%d",&a[i]);
}
build(1,N,1);
while(M--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",bsearch(l,r,k));
}
return 0;
}