【归并树—求第K小数】POJ 2104/HDU 2665

归并树 O(logn*logn*logn) for each query,很慢的说,划分树版http://blog.csdn.net/leolin_/article/details/6696801

/*
//1,建立归并树后我们得到了序列key[]的非降序排列,由于此时key[]内元素的rank是非递
减的,因此key[]中属于指定区间[s,t]内的元素的rank也是非递减的,所以我们可以用二分法
枚举key[]中的元素并求得它在[s,t]中的rank值,直到该rank值和询问中的rank值相等;
//2,那对于key[]中的某个元素val,如何求得它在指定区间[s,t]中的rank?这就要利用到刚
建好的归并树:我们可以利用类似线段树的query[s,t]操作找到所有属于[s,t]的子区间,然后
累加val分别在这些子区间内的rank,得到的就是val在区间[s,t]中的rank,注意到这和合
并排序的合并过程一致;
//3,由于属于子区间的元素的排序结果已经记录下来,所以val在子区间内的rank可以通过
二分法得到。
//上面三步经过了三次二分操作(query也是种二分),于是每次询问的复杂度是O(log n *
log n * log n)
*/
#define N 100005
#define LogN 18
int seq[LogN][N];
int a[N];
struct node{
    int l,r;
}t[N*3];
void build(int id,int l,int r,int dep){
    t[id].l = l;
    t[id].r = r;
    if(l == r){
        seq[dep][l] = a[l];
        return ;
    }
    int mid = (l+r)>>1;
    build(id<<1,l,mid,dep+1);
    build(id<<1|1,mid+1,r,dep+1);
    /*Merge Sort*/
    int i,j,cnt;
    i = l,j = mid+1,cnt = l;
    while(i<=mid && j<=r){
        if(seq[dep+1][i] < seq[dep+1][j]){
            seq[dep][cnt++] = seq[dep+1][i++];
        } else {
            seq[dep][cnt++] = seq[dep+1][j++];
        }
    }
    if(i == mid+1){
        while(j<=r){
            seq[dep][cnt++] = seq[dep+1][j++];
        }
    } else {
        while(i<=mid){
            seq[dep][cnt++] = seq[dep+1][i++];
        }
    }
}
int query(int id,int l,int r,int val,int dep){
    if(l<=t[id].l && t[id].r<=r){
        return lower_bound(&seq[dep][t[id].l], &seq[dep][t[id].r] + 1, val) - &seq[dep][t[id].l];//*
    }
    int mid = (t[id].l+t[id].r)>>1;
    int res = 0;
    if(l<=mid){
        res += query(id<<1,l,r,val,dep+1);
    }
    if(r>mid){
        res += query(id<<1|1,l,r,val,dep+1);
    }
    return res;
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m) != -1){
        int i,j;
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n,1);
        while(m--){
            int s,t,k;
            scanf("%d%d%d",&s,&t,&k);
            k--;//*求第k大的话只要把这行改为k = t-s+1-k即可
            int l = 1,r = n,mid;
            while(l<r){
                mid = (l+r+1)>>1;//*
                int pos = query(1,s,t,seq[1][mid],1);
                if(pos<=k){
                    l = mid;
                } else r = mid-1;
            }
            printf("%d\n",seq[1][l]);
        }
    }
    return 0;
}

http://codeforces.com/contest/85/problem/D——线段树,有空过了它






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值