hdu 2795 Billboard -- 线段树(单点更新,求区间最大值)

题目链接:hdu 2795 Billboard


                                             Billboard

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


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的海报板,有n个 1*wi的海报,从上向下依次贴,如果海报能贴在板子上输出行数,不能输出-1

  思路 :

           这其实是一个线段树,单点更新,区间查询,取最大值的线段树基础题。  
          因为 1 <= h<= 10^9; 1 <= n <= 200,000; 每一行海报高为1,所以可以将海报板分为h个,高为1的小海报板 。
          因为每一个海报高为1,所以就算每一行小海报板只能贴一张,当所有海报都贴在海报板上,所占的总高度也不过是n,因    此当h远大于n话,则h将没有存在的意义 。
          所以可以选择,H = min(h,n),取h,n中数值较小的数值H来建树,树的区间为[1,H], Bulid(1, H, 1); 而每一个叶节点[1,1],[2,2] .... [H,H]的大小初值为w,代表着每一行的最大容量空间为w,一共H行。
          每贴一张海报,就从[1,H],即1~H个小海报板中寻找能够贴下海报的第i行小海报板,将海报贴下,并将该行对应得叶节点[i,i]的最大容量进行更新。
         事时更新区间的最大值,如果要贴的海报所占的容量,大于该区间所有小海报板的最大容量,则此区间无该海报的容身之地,改变区间再进行查找。
        从[1, H] -> [1,H/2],[H/2 + 1]  -> [1, H/4] ,[H/4 + 1, H/2],[H/2 + 1, 3H/4], [3H/4 + 1, H] ...... -> [1,1], [2,2] ....[H, H] 。
        区间折半查询,直到找到最大容量大于海报容量的空间,并找到相对应的叶节点将海报贴下,更新叶节点最大容量空间,更新叶节点所在区间的最大容量。

 

     AC代码:

#include <iostream>
#include <stdio.h> 
#include <algorithm>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int Max = 2e5 + 5;
long str[Max << 2];
int h, w, n;
 
void pushDate(int rt)
{
	str[rt] = max (str[lson], str[rson]); //事时更新父节点的最大值 
}

void Bulid(int l, int r, int rt)
{
	str[rt] = w; // 为每一个节点赋初值为w 
	if(l == r) return ;
	int m = (l + r) >> 1;
	Bulid(l, m, lson);
	Bulid(m+1, r, rson);
}

int Query(int p, int l, int r, int rt)
{
	if(l == r)
	{
		str[rt] -= p; //更新叶节点容量的最大值 
		return l;
	} 
	int temp = 0;
	int m = (l + r) >> 1;
	if(p <= str[lson])  //检测左子树的最大容量容下容量为 p的海报 
		temp = Query(p, l, m, lson);
	else  temp = Query(p, m+1, r, rson); // 区间折半查找左子树空间不够,就向右子树查询 
	pushDate(rt); // 事时更新父节点,比较左右子树,将最大储存空间赋予父节点,以便进行查询 
	return temp; 
}

int main()
{
	while(scanf("%d%d%d",&h, &w, &n) != EOF)
	{
		h = min(h, n); // 取h,n中的较小值建树 
		Bulid(1, h, 1);
		while(n--)
		{
			int a;
			scanf("%d",&a);
			if(a > str[1]) printf("-1\n");//如果代表整个海报板所有行中最大容纳空间的祖宗节点都无法容纳要贴的海报,那整个海报板就无海报的容纳之地 
			else printf("%d\n",Query(a, 1, h, 1));
		}
	}
	
	return 0;
}
 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值