HDU 2795 Billboard(线段树,单点更新)

D - Billboard
Crawling in process... Crawling failed Time Limit:8000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Appoint description:

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.
 

Sample Input

3 5 5 2 4 3 3 3
 

Sample Output

1 2 1 3 -1
 
题目大意:
  这道题是说,给你一个h*w的矩形,然后有n个大小为1*w_i的海报,每次贴海报的时候,遵循一个原则,就是尽量往高的地方贴,尽量往左贴。每次贴一个海报,输出海报所在的行号。如果贴不下来,就输出-1。
 
解题思路:
  看过题目后,并没有想到该怎么建树,然后看了题解后,才明白了到底该怎么做,也就是说n<=200000,那么,h的取值范围只能是1~200000之间,当h<200000的时候,我们就应该build(1,1,h)。
当h>=200000,我们就应该build(1,1,200000),建线段树的过程中,我们知道每个叶子节点维护的应该是长度为w的区间,因为每行的长度在没有贴海报的时候就是w。接下来就是我们所说的更新操作了,更新操作的时候,我们要注意,如果第1行可以贴进去的话,那么我们就把目前的val贴进去,然后相应的第一行所代表的叶子节点的mx就要变tree[].mx-=val。如果第一行插不进去了,也就是说,在找到他的父亲节点的时候,出现了tree[lid].mx<val,那么我们就去看tree[rid].mx和val的大小关系,如果tree[rid].mx > val ,那么就把当前的这个val插入到右儿子去。再次更新mx,然后把左儿子和右儿子的信息回溯到父亲节点,push_up(id)。按照这个思路,递归的走完所有的子树就行了。每次返回的行号其实就是叶子节点的左右区间任意一个的标号。
 
代码:
# include<cstdio>
# include<iostream>

using namespace std;

int h,w,n;
# define MAX 200004
# define lid id<<1
# define rid id<<1|1

struct Segtree
{
    int l,r;
    int mx;
}tree[MAX*4];

void push_up ( int id )
{
    tree[id].mx=max(tree[lid].mx,tree[rid].mx);
}

void build( int id,int l,int r )
{
    tree[id].l = l; tree[id].r = r;
    tree[id].mx = w;
    if ( l==r )
    {
        return;
    }
    int mid = ( tree[id].l+tree[id].r )/2;
    build(lid,l,mid);
    build(rid,mid+1,r);
    push_up(id);
}

int query( int id,int val )
{
    if ( tree[id].l==tree[id].r )
    {
        tree[id].mx-=val;
        return tree[id].l;
    }
    int pos = 0;
    if ( tree[lid].mx >= val )
        pos = query(lid,val);
    else
        pos = query(rid,val);
    push_up(id);
    return pos;
}

int main(void)
{
    while ( scanf("%d%d%d",&h,&w,&n)!=EOF )
    {
        if ( h > MAX )
            h = MAX;
        build(1,1,h);
        for ( int i = 0;i < n;i++ )
        {
            int tmp; scanf("%d",&tmp);
            if ( tree[1].mx < tmp )
                printf("-1\n");
            else
            {
                int ans = query(1,tmp);
                printf("%d\n",ans);
            }
        }
    }

    return 0;
}

  

 
 
 
 
          
posted @ 2015-08-19 11:39  BYYB_0506  阅读(164)  评论(0编辑  收藏  举报