hdu 4471 区间条件统计 区间 不超过 x 的元素的个数

题目传送门//res tp hdu

目的

对长度为n的区间,m次询问,每次提供一个区间两端点与一个值x,求区间内不超过x的元素个数

n 1e5
m 1e5
ai [1,1e9] (i∈[1,n])
多测

数据结构与……?

划分树 + 二分查找

分析

建树0(nlogn)
单次查询第k小,需O(logn)
蛮力枚举区间内所有元素,查看是否大于x,需O(nlogn),m次询问,总O(mnlogn),显然会超时。

考虑以二分x的方式枚举出不超过x的最大上界只需O(mlogxlogn)。这种方法是可接受的。

#include<iostream>
#include<algorithm>
using namespace std;
const int L = 100010;
int tr[18][L],cnt[18][L],n,m,sor[L];

void build(int de,int l,int r){
    if(l == r) return;
    int mi = (l + r)>>1;
    int scnt = mi - l + 1;
    int M = sor[mi];
    for(int i = l;i<=r;++i) if(tr[de][i] < M) scnt--;
    int pl = l,pr = mi + 1;
    for(int i = l,cntl = 0;i<=r;++i){
        int num = tr[de][i];
        if(num < M ||(num == M && scnt > 0)){
            if(num == M) --scnt;
            ++cntl;
            tr[de+1][pl++] = num;
        }
        else tr[de+1][pr++] = num;
        cnt[de][i] = cntl;
    }
    build(de+1,l,mi);
    build(de+1,mi+1,r);
}
int query(int de,int L,int R,int l,int r,int k){
    if(L == R) return tr[de][L];
    int mi = (L + R)>>1;
    int left = 0,cnt_in_left = cnt[de][r];
    if(L != l){
        left = cnt[de][l-1];
        cnt_in_left -= left;
    }
    if( k <= cnt_in_left ){
        int newl = L + left;
        int newr = newl + cnt_in_left - 1;
        return query(de+1,L,mi,newl,newr,k);
    }
    else{
        int a = l - L - left;
        int b = r - l + 1 - cnt_in_left;
        int newl = mi + 1 + a;
        int newr = newl + b - 1;
        return query(de+1,mi+1,R,newl,newr,k - cnt_in_left);
    }
}
int Q(int l,int r,int h){
    int lo = 1,hi = r - l + 1;
    while(lo <= hi){
        int mi = (lo + hi)>>1;
        int M =query(0,1,n,l,r,mi);
        if(h < M ) hi = mi-1;
        else lo = mi + 1;
    }
    return --lo;
}
int main(){
    int T,icase = 1,l,r,h,ans;
    scanf(" %d",&T);
    while(T--){
        scanf(" %d %d",&n,&m);
        for(int i = 1;i<=n;++i){
            scanf(" %d",&sor[i]);
            tr[0][i] = sor[i];
        }
        sort(sor+1,sor+1+n);
        build(0,1,n);
        printf("Case %d:\n",icase++);
        while(m--){
            scanf(" %d %d %d",&l,&r,&h);
            ans = Q(l+1,r+1,h);
            printf("%d\n",ans);
        }
    }
}


转载于:https://www.cnblogs.com/tea-egg/p/11248877.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值