划分树

看了一晚上hh大牛的划分树,总算看懂了是怎么回事了。。

hh大牛的博客,请戳这里

划分树是快排+线段树

先来一模版 POJ-2104-K-th Number

题意:给出n,m,n代表N个数,m代表查询的次数,再输入m行l,r,k;求在[l,r]区间中第k小的数;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100050
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
int tt[MAXN<<2];//呃,因为不要统计区间的操作,所以这个线段树一般有的数组,现在没有用了。。
int sorted[MAXN];
int toLeft[20][MAXN];//20代表的是20层
int val[20][MAXN];
void build(int d,int l,int r,int rt){
    if(l==r)return;
    int mid=(l+r)>>1;
    int lsame=mid-l+1;//lsame表示和val_mid相等且分到左边的
    for(int i=l;i<=r;i++){
        if(val[d][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){
            toLeft[d][i]=0;//toLeft[i]表示[ l , i ]区域里有多少个数分到左边
        }
        else{
            toLeft[d][i]=toLeft[d][i-1];
        }
        if(val[d][i]<sorted[mid]){
            toLeft[d][i]++;
            val[d+1][lpos++]=val[d][i];
        }
        else if(val[d][i]>sorted[mid]){
            val[d+1][rpos++]=val[d][i];
        }
        else{
            if(same<lsame){//有lsame的数是分到左边的
                same++;
                toLeft[d][i]++;
                val[d+1][lpos++]=val[d][i];
            }
            else{
                val[d+1][rpos++]=val[d][i];
            }
        }
    }
        build(d+1,lson);
        build(d+1,rson);
}
int query(int L,int R,int k,int d,int l,int r,int rt){
    if(l==r){
        return val[d][l];
    }
    int s;//s表示[ L , R ]有多少个分到左边
    int ss;//ss表示 [l , L-1 ]有多少个分到左边
    int mid=(l+r)>>1;
    if(L==l){
        s=toLeft[d][R];
        ss=0;
    }
    else{
        s=toLeft[d][R]-toLeft[d][L-1];
        ss=toLeft[d][L-1];
    }
    if(s>=k){//有多于k个分到左边,显然去左儿子区间找第k个
        int newl=l+ss;
        int newr=l+ss+s-1;//计算出新的映射区间
        return query(newl,newr,k,d+1,lson);
    }
    else {
        int bb=L-l-ss;//bb表示 [l , L-1 ]有多少个分到右边
        int b=R-L-s+1;//b表示 [L , R]有多少个分到右边
        int newl=mid+bb+1;
        int newr=mid+bb+b;
        return query(newl,newr,k-s,d+1,rson);
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[0][i]);
        sorted[i]=val[0][i];
    }
    sort(sorted+1,sorted+n+1);
    build(0,1,n,1);
    while(m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(l,r,k,0,1,n,1));
    }
    return 0;
}
posted @ 2012-08-14 00:25  z.arbitrary  阅读(275)  评论(0编辑  收藏  举报