POJ 2104 & HDU 2665 K-th Number (划分树)

题意不用说了,就是询问x次,指定区间的第k小的值....................


表示太弱了.......看了许久还是不太理解划分树内涵,先把偷来的模板留着

#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define L(x) x << 1
#define R(x) x << 1 | 1
using namespace std;


struct Seg_Tree {
    int l,r,mid;
} tr[MAX*4];
int len;
int sorted[MAX];
int lef[20][MAX];
int val[20][MAX];

void build(int l,int r,int step,int x) {
    tr[x].l = l;
    tr[x].r = r;
    tr[x].mid = (l + r) >> 1;
    if(tr[x].l == tr[x].r) return ;
    int mid = tr[x].mid;
    int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的
    for(int i = l ; i <= r ; i ++) {
        if(val[step][i] < sorted[mid]) {
            lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
        }
    }
    int lpos = l;
    int rpos = mid + 1;
    int same = 0;
    for(int i = l ; i <= r ; i ++) {
        if(i == l) {
            lef[step][i] = 0;//lef[i]表示[ tr[x].l , i ]区域里有多少个数分到左边
        } else {
            lef[step][i] = lef[step][i-1];
        }
        if(val[step][i] < sorted[mid]) {
            lef[step][i] ++;
            val[step + 1][lpos++] = val[step][i];
        } else if(val[step][i] > sorted[mid]) {
            val[step+1][rpos++] = val[step][i];
        } else {
            if(same < lsame) {//有lsame的数是分到左边的
                same ++;
                lef[step][i] ++;
                val[step+1][lpos++] = val[step][i];
            } else {
                val[step+1][rpos++] = val[step][i];
            }
        }
    }
    build(l,mid,step+1,L(x));
    build(mid+1,r,step+1,R(x));
}

int query(int l,int r,int k,int step,int x) {
    if(l == r) {
        return val[step][l];
    }
    int s;//s表示[l , r]有多少个分到左边
    int ss;//ss表示 [tr[x].l , l-1 ]有多少个分到左边
    if(l == tr[x].l) {
        s = lef[step][r];
        ss = 0;
    } else {
        s = lef[step][r] - lef[step][l-1];
        ss = lef[step][l-1];
    }
    if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个
        int newl = tr[x].l + ss;
        int newr = tr[x].l + ss + s - 1;//计算出新的映射区间
        return query(newl,newr,k,step+1,L(x));
    } else {
        int mid = tr[x].mid;
        int bb = l - tr[x].l - ss;//bb表示 [tr[x].l , l-1 ]有多少个分到右边
        int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边
        int newl = mid + bb + 1;
        int newr = mid + bb + b;
        return query(newl,newr,k-s,step+1,R(x));
    }
}

int n,m,l,r,k;
int main() {
    while(scanf("%d%d",&n,&m) != EOF) {
        for(int i=1; i<=n; i++) {
            scanf("%d",&val[0][i]);
            sorted[i] = val[0][i];
        }
        sort(sorted + 1,sorted + 1 + n);
        build(1,n,0,1);
        for(int i=0; i<m; i++) {
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",query(l,r,k,0,1));
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值