poj 2104 K-th Number(线段树)

来源:互联网 发布:零之镇魂曲 知乎 编辑:程序博客网 时间:2024/06/10 18:45

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


0 0
原创粉丝点击