hdu 4417(划分树+二分)

本文介绍了一种使用划分树算法在静态区间内查找所有小于等于指定值H的数的数量的方法。通过二分查找定位第一个大于H的数,从而确定小于H的数的个数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求一个静态区间里,小于等于H的数有多少个。

二分第k小,用划分树找到第一个大于H的数,它是第K小,那么就有K-1个数小于H。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 101000
#define MID(a,b) (a+((b-a)>>1))
int n,m;
int sorted[MAX];
struct ptree
{
    int val[MAX];
    int num[MAX];
};
ptree tree[20];
void build_tree(int l,int r,int p)
{
    int i,j;
    if(l==r) return ;
    int mid=MID(l,r);
    int lsame=mid-l+1,same=0,ln=l,rn=mid+1;
    for(i=l;i<=r;i++) if(tree[p].val[i]<sorted[mid]) lsame--;
    for(i=l;i<=r;i++)
    {
        if(i==l) tree[p].num[i]=0;
        else tree[p].num[i]=tree[p].num[i-1];
        if(tree[p].val[i]<sorted[mid])
        {
            tree[p].num[i]++;
            tree[p+1].val[ln++]=tree[p].val[i];
        }
        else if(tree[p].val[i]>sorted[mid])
        {
            tree[p+1].val[rn++]=tree[p].val[i];
        }
        else
        {
            same++;
            if(lsame>=same)
            {
                tree[p].num[i]++;
                tree[p+1].val[ln++]=tree[p].val[i];
            }
            else
            {
                tree[p+1].val[rn++]=tree[p].val[i];
            }
        }
    }
    build_tree(l,mid,p+1);
    build_tree(mid+1,r,p+1);
}
int query(int l,int r,int left,int right,int k,int p)
{
    if(left==right) return tree[p].val[left];
    int mid=MID(left,right);
    int lx,ly,rx,ry;
    /*
        lx表示从lft到st-1这段区间内有多少个数进入左子树
        ly表示从st到ed这段区间内有多少个数进入左子树
        rx表示从lft到st-1这段区间内有多少个数进入右子树
        ry表示从st到ed这段区间内有多少个数进入右子树
     */
    if(l==left)
    {
        lx=0;ly=tree[p].num[r];
    }
    else{
        lx=tree[p].num[l-1];ly=tree[p].num[r]-lx;
    }
    if(k<=ly)
    {
        l=left+lx;
        r=left+lx+ly-1;
        return query(l,r,left,mid,k,p+1);
    }
    else
    {
        rx=l-left-lx;
        ry=r-l+1-ly;
        l=mid+rx+1;
        r=mid+rx+ry;
        return query(l,r,mid+1,right,k-ly,p+1);
    }
}
int main()
{
    int i,j,k;
    int L,R,H;
    int T,casei=0;
    int num;
    scanf("%d",&T);
    while(T--)
    {
        casei++;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {scanf("%d",&tree[0].val[i]);sorted[i]=tree[0].val[i];}
        sort(sorted+1,sorted+n+1);
        build_tree(1,n,0);
        printf("Case %d:\n",casei);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&L,&R,&H);
            L++;R++;
            num=R-L+1;
            if(query(L,R,1,n,num,0)<=H) {printf("%d\n",num);continue;}
            if(query(L,R,1,n,1,0)>H) {printf("0\n");continue;}
            int Min=1,Max=num,mid,pos;
            while(Min<Max)
            {
                mid=(Min+Max)>>1;
                int temp=query(L,R,1,n,mid,0);
                if(temp<=H) {Min=mid+1;pos=Min;}
                else {Max=mid;pos=Max;}
            }
            printf("%d\n",pos-1);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值