HDU 2795 Billboard(线段树:找到线段树中>=给定值的第一个元素位置 并 更新该点)

HDU 2795 Billboard(线段树:找到线段树中>=给定值的第一个元素位置 并 更新该点)

http://acm.hdu.edu.cn/showproblem.php?pid=2795

Problem Description
At the entrance to the university, there is a huge rectangular billboard of size h*w (h is its height and w is its width). The board is the place where all possible announcements are posted: nearest programming competitions, changes in the dining room menu, and other important information.

On September 1, the billboard was empty. One by one, the announcements started being put on the billboard.

Each announcement is a stripe of paper of unit height. More specifically, the i-th announcement is a rectangle of size 1 * wi.

When someone puts a new announcement on the billboard, she would always choose the topmost possible position for the announcement. Among all possible topmost positions she would always choose the leftmost one.

If there is no valid location for a new announcement, it is not put on the billboard (that's why some programming contests have no participants from this university).

Given the sizes of the billboard and the announcements, your task is to find the numbers of rows in which the announcements are placed.
 

Input
There are multiple cases (no more than 40 cases).

The first line of the input file contains three integer numbers, h, w, and n (1 <= h,w <= 10^9; 1 <= n <= 200,000) - the dimensions of the billboard and the number of announcements.

Each of the next n lines contains an integer number wi (1 <= wi <= 10^9) - the width of i-th announcement.
 

Output
For each announcement (in the order they are given in the input file) output one number - the number of the row in which this announcement is placed. Rows are numbered from 1 to h, starting with the top row. If an announcement can't be put on the billboard, output "-1" for this announcement.

分析:

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

       最多只有n(n<=20W)个公共,且一条公共如果连一整行都放不下(即wi>w),直接输出-1即可。所以我们线段树最多只需要维护20W个元素。就算h有10亿,后面那些行根本是浪费。

       注意:如果hn,那么线段树的节点总数定为h,否则定位n.如果wi>w,那么直接输出-1.

       现在我们依次读入wi,找到能放下wi的序号最小的线段树叶节点,然后更新线段树即可。这个查询过程我们可以写query函数来查询,当然也可以用如下做法:将query嵌入update中。

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

       本线段树不用query,只需要update并在update的时候返回需要的信息即可。

AC代码:2437ms

<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)
{
    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>


你可能感兴趣的:(ACM)