hdu2795 线段树应用:找到线段树中>=给定值的第一个元素位置 并 更新该点)

问题描述

在大学的入口处,有一个巨大的矩形广​​告牌,大小为h * w(h是它的高度,w是它的宽度)。董事会是发布所有可能公告的地方:最近的节目比赛,餐厅菜单的变化以及其他重要信息。

9月1日,广告牌是空的。一个接一个,公告开始被放在广告牌上。

每个公告都是单位高度的纸条。更具体地,第i个通告是大小为1 * wi的矩形。

当有人在广告牌上发布新的公告时,她总是会选择公告的最高位置。在所有可能的最高职位中,她总是选择最左边的职位。

如果新公告没有有效位置,则不会将其放在广告牌上(这就是为什么某些编程竞赛没有来自该大学的参与者)。

根据广告牌和公告的大小,您的任务是查找公告所在的行数。

 

输入

有多种情况(不超过40例)。

输入文件的第一行包含三个整数,h,w和n(1 <= h,w <= 10 ^ 9; 1 <= n <= 200,000) - 广告牌的尺寸和公告数量。

接下来的n行中的每一行包含整数wi(1 <= wi <= 10 ^ 9) - 第i个通告的宽度。

 

产量

对于每个公告(按输入文件中给出的顺序)输出一个数字 - 放置此公告的行号。从第一行开始,行从1到h编号。如果无法在广告牌上刊登公告,请为此公告输出“-1”。

样本输入
3 5 5
2
4
3
3
3
 

样本输出
1
2
1
3
-1

分析:

 建立一颗线段树,线段树维护的每个元素(不是指树的节点哦)代表广告牌的一行的当前剩余最大空间maxv。比如i节点维护区间[l,r],那么k(l<=k<=r)代表广告牌的第k行。即maxv[i]就是i节点维护的广告牌的那些行中,剩余空间的最大值。

 其中线段树中节点维护的信息是:本节点控制的区间[L,R]内的叶节点的最大剩余空间maxv[i]。如果maxv[i]>wi说明这个节点的子树有叶子能放下wi,优先往左子树找即可.

代码:
 

<span style="font-size:18px;"><span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int H,W,n,Q;
const int MAXN=200000+100;
int maxv[MAXN*4];
int id[MAXN*4];
int cnt;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
void PushUp(int i)
{
    maxv[i]=max(maxv[i*2] , maxv[i*2+1]);
}
void build(int i,int l,int r)
{
    if(l==r)
    {
        maxv[i]=W;
        id[i]=++cnt;
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    PushUp(i);
}
int update(int w,int i,int l,int r)  //这里的跟新就是找到大于等于w的最小的那个叶节点。
{
    if(w>maxv[i])
        return -1;
    if(l==r)
    {
        maxv[i]-=w;
        return id[i];
    }
    int res=-1;
    int m=(l+r)/2;
    if(maxv[i*2]>=w)res = update(w,lson);//这里优先考虑左子树。
    else if(maxv[i*2+1]>=w)res= update(w,rson);
    PushUp(i);
    return res;
}
int main()
{
    while(scanf("%d%d%d",&H,&W,&Q)==3)
    {
        cnt=0;
        n = min(Q,H);//线段树叶节点的最大数目
        build(1,1,n);
        for(int i=1;i<=Q;i++)
        {
            int w;
            scanf("%d",&w);
            printf("%d\n",update(w,1,1,n));
        }
    }
    return 0;
}</span></span>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值