hdu2795 线段树

Billboard

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

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
 题目大意:有一个广告板,高为h,宽为w,现在你要往这个广告板上放广告,每一个广告所占的面积为1*Wi,放置的规则为优先往上面放,并且同一行优先往
左边放,然后给你n个wi,依次输出这些广告所在的行数。
思路分析:刚学线段树没多久,看到这道题的分类是在线段树下,就做了一下,刚开始我是有几分蒙比的,感觉和之前做的几道题有所不同,首先是线段树的位置,
以前的线段树都是建立在n上的,而本题的线段树则应该建在h上,1-h表示行数,而节点需要维护的信息是接下来要考虑的内容,依照本题题意,维护的应该是该行
剩余的区间长度,而在子节点与根节点的关系我又有几分蒙比,后来想明白了,本题中线段树的建立主要是要提供一种高效的查找方法--二分查找,这也是本题为何
使用线段树的原因,但是本题要返回行数,则每一次查找都要到根节点,这样无疑是比较耗时的,一般会超时,但是本题的输入比较少,不超过40个例子,同时时限
放的比较宽,足足有8000ms,可以放心的用线段树了,另外以往题目往往是让返回节点储存的信息,而本题则是要返回节点的位置,这也是以后做题需要注意的地方
以后要有这种意识。对于线段树,确定好建在哪里,节点维护的信息,基本后面的都是水到渠成了。
tips:1.本题建树查找一定要先左后右这样才符合优先往上面放的题意;
       2.对于线段长度,如果按题意的话,应该是h的上限10^9,但这样无疑行不通,思考一下,一共只有20w广告,如果前20w行仍然放不了这些广告,这说明什么
   说明某些广告宽度>w,这样的话就算是再有其他行也没有用了,所以maxn取20w就可以,但是注意,建树是否要1-maxn?,如果h<maxn,当然不需要,但如果
 >maxn,就需要进行替换,令h=maxn,要不然会re.
代码:#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=200000+100;
int tree[3*maxn];
int h,w;
void build(int p,int l,int r)
{
    tree[p]=w;
    if(l==r) return;
    int mid=(l+r)/2;
    build(2*p,l,mid);
    build(2*p+1,mid+1,r);
}
int find(int p,int l,int r,int b)
{
    int line;
    if(l==r){tree[p]-=b;return l;}
    int mid=(l+r)/2;
    if(tree[2*p]>=b) line=find(2*p,l,mid,b);
    else if(tree[2*p+1]>=b) line=find(2*p+1,mid+1,r,b);
    else line=-1;
    tree[p]=max(tree[2*p],tree[2*p+1]);//update
    return line;
}
int main()
{
    int n,l;
    while(scanf("%d%d%d",&h,&w,&n)!=EOF)
    {
        if(h>maxn) h=maxn;
        build(1,1,h);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&l);
            printf("%d ",find(1,1,h,l));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xuejianye/p/5389799.html