poj 2104 K-th Number(线段树)

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


你可能感兴趣的:(线段树,二分搜索)