求区间第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; }