poj 2104 (求区间第K大元素)

//为了做一道题,以前不知道什么划分树,只能从最基本的学,这是入门题,看了别人了代码,然后慢慢写的
#include <iostream>
using namespace std ;
const int MAXN = 100010 ;
int data[30][MAXN], sorted[MAXN], toleft[30][MAXN] ;
int cmp(const void *a, const void *b){
    return *(int *)a < *(int *)b ? -1 : 1 ;
}
void build(int l, int r, int d){
    if(l==r)    return ;
    int i ;
    int m = (l + r) >> 1 ; 
    int ls = m - l + 1 ;   //用来标记和中间值m相等的,且分到左孩子的数的个数。
	                       //初始时,假定当前区间[l,r]有mid-l+1个和m 相等。 
                 	       //先踢掉比中间值小的,剩下的就是要插入到左边的*/
    for(i=l; i<=r; i++)   
		if(data[d][i]<sorted[m])    
			ls -- ;         //踢掉比中间值小的
		int lp = l ; 
		int rp = m + 1 ;
		for(i=l; i<=r; i++){      
			if(i==l)   
				toleft[d][i] = 0 ;   初始一个子树。
			else       
				toleft[d][i] = toleft[d][i-1] ;   初始区间下一个节点。

			/* 如果大于,肯定进入右孩子,否则,判断是否还有相等的应该进入左孩子的, 
			   没有,就直接进入右孩子,否则进入左孩子*/
			if(data[d][i]<sorted[m]){
				toleft[d][i]++;
				data[d+1][lp++] = data[d][i] ;
			}else if(data[d][i]>sorted[m])
				data[d+1][rp++] = data[d][i] ;
			else{                      //考虑相等
				if(ls){               // 相等分到做孩子的数目>0
					ls -- ;
					toleft[d][i]++ ;
					data[d+1][lp++] = data[d][i] ;
				}else data[d+1][rp++] = data[d][i] ;
			}
		}
		build(l, m, d+1) ;
		build(m+1, r, d+1) ;
}
int query(int L, int R, int k, int l, int r, int d){  //在[L,R]区间寻找小于第k 的元素,总区间[l,r]
    if(L==R)    return data[d][L] ;
    int m = (l + r) >> 1 ;
    int lLR, llL, rLR, rlL ;   
    if(L==l){
        lLR = toleft[d][R] ;   //lLR 左边小于第k个元素的数量
        llL = 0 ;             //llL 记录区间[lft, a-1)中计入左孩子的元素的个数。
    }else{
        lLR = toleft[d][R] - toleft[d][L-1] ; //
        llL = toleft[d][L-1] ;
    }
    int nl, nr ;
    if(lLR>=k){
        nl = l + llL ;       //记录区间[L, R]中进入左孩子的元素的个数。
        nr = l + lLR + llL - 1 ;  //
        return query(nl, nr, k, l, m, d+1) ;
    }else{
        rlL = L - l - llL ;       //表示[L, R] 中分到右孩子的个数
        rLR = R - L + 1 - lLR ;  //表示[lft, a-1]中分到右孩子的个数
        nl = m + 1 + rlL ;
        nr = m + rlL + rLR ;
        return query(nl, nr, k-lLR, m+1, r, d+1) ;
    }
}

int main(){
    int n, m, i, j, l, r, k ;
    while(~scanf("%d%d",&n,&m)){
        for(i=1; i<=n; i++){
            scanf("%d",&data[0][i] );
            sorted[i] = data[0][i] ;
        }
        qsort(sorted+1, n, sizeof(int), cmp) ;
        build(1, n, 0);
        while(m--){
            scanf("%d%d%d", &l, &r, &k) ;
            printf("%d\n",query(l, r, k, 1, n, 0)) ;
        }
    }
    return 0 ;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值