[ACM] hdu 2795 Billboard (线段树)

Billboard

Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8962    Accepted Submission(s): 3997


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.
 


 

Sample Input
3 5 5 2 4 3 3 3
 


 

Sample Output
1 2 1 3 -1
 


 

Author
hhanger@zju
 


 

Source

解题思路:

题意为有一块宽为L高为H的板子,上面要贴宽为w,高度统一为1的通知单。高度为1,这样板子就可以从上到下分为H行,编号1到H,依次地放传单,每次都尽可能地向上,向左放,输出每个通知单放的位置(即在板子的第几行),如果板子上没有位置可放,就输出-1. 用线段树来做,首先建树,叶子节点数为板子的高度即行数,节点维护区间高度的最大“空隙”,即(放了通知单以后,行内还有多少宽度可以使用的最大值)。因为要尽可能地向上放,所以左儿子优先于右儿子优先访问,放每个宣传单时,找到节点维护的最大值大于等于该宣传单的宽度时,就判断该最大值来自左儿子还是右儿子,如果两者一样,优先左儿子,如果左儿子是叶子节点,则它存放的宽度值(也是它当前的最大值,因为它没有子节点)减去该宣传单的宽度,然后向上更新父节点。

做了几道线段树的题目,觉得首先要弄清楚叶子节点代表的是什么,并依此建树,毕竟,建树是第一步,每个节点维护的是什么,实现什么样的功能。

代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn=200005;
int Max[maxn<<2];
int h,w,n;

void PushUp(int rt)//维护最大”空隙“
{
    Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
void build(int l,int r,int rt)//高度有多少,建立的树就有多少个叶子节点,每个节点代表高度中的每一行
{
    /*if(l==r)
    {
        m[rt]=w;
        return;
    }*/ //错误代码,每个节点都得赋值为w
    Max[rt]=w;
    if(l==r)
        return;
    int m=(r+l)>>1;
    build(lson);
    build(rson);
    //PushUp(rt);//不用加这一句话。
}
int query(int x,int l,int r,int rt)
{
    if(l==r)
    {
        //m[l]-=x;//错误
        Max[rt]-=x;//当前节点
        return l;
    }
    int m=(r+l)>>1;
    //int ans=0;//错误,不能赋值
    int ans;
    ans=(Max[rt<<1]>=x)?query(x,lson):query(x,rson);//优先查询左儿子
    PushUp(rt);//更新
    return ans;
}
int main()
{
    while(scanf("%d%d%d",&h,&w,&n)==3)
    {
        h=min(h,n);//当查询的个数n小于h时,h=n的情况是一样的,因为下面高度的板子用不到,每行一个就足够了
        build(1,n,1);
        int x;
        while(n--)
        {
            scanf("%d",&x);
            if(Max[1]<x)//所有行的最大空还小于x,放不下
            {
                printf("-1
");
            }
            else
                printf("%d
",query(x,1,h,1));
        }
    }
    return 0;
}


 

原文地址:https://www.cnblogs.com/sr1993/p/3697900.html