首页 > 代码库 > hdu 2795-Billboard

hdu 2795-Billboard

Billboard

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


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
 【题意】: 给你一块 w*h 的板,然后让你用一些 1*x 的木块去填,每次填的时候总是选择行靠前的,如果可以放在该行上,则会选择列靠前的,现在让你对每一种木块 输出它所放在的行,如果不能放上去则输出 -1

【分析】: 这题一开始看到数据范围是 10^9 吓了一跳,不过仔细分析可以发现,其实真正有用的只是 min(h,n) 其它都是浪费的,那么好了,就可以往下做了 很明显的是一个区间染色问题,也很容易的就想到可以用线段树来搞 不过这题是分成若干段后的染色,一开始可能会觉得可能需要建立多个线段树来针对每一段分别来搞 实际上,这题只需要一个线段树就可以搞定了 建一棵 [1,len] len=min(h,n) 的线段树,每一个线段维护一个最大长度域 maxLen(这里由于不同组之间的线段是不能拼在一起的,因此维护的时候只要简单的 max 一下就行了,不需要左右的讨论,再维护两个域 lmax,rmax),一开始的时候 maxLen 都为 w ,然后维护这个域的时候只需要简单的 max 一下就行了(每一行其实都是独立的,用线段树维护的只是可以方便的找到最优的行的位置) 之后就是利用二分逼近的思想来查找那个符合条件的最优行的位置,找到之后做出相应的修改,即 update 该行的 maxLen 域就行了 
代码:
#include<cstdio> 
#include<iostream>
using namespace std;
#define maxn 200005 
#define L(i) (i<<1) 
#define R(i) (i<<1|1) 
int h,w,n; 
struct node 
{
	int left,right,len;
};
inline int max(int a,int b)
{
	return a>b?a:b;
}
inline int min(int a,int b)
{
	return a<b?a:b;
}
struct SegTree 
{ 
	node tree[maxn*4];
	void update(int p)
	{ 
		tree[p].len=max(tree[L(p)].len,tree[R(p)].len);
	}
	void build(int p,int left,int right)
	{
		tree[p].left=left;
		tree[p].right=right;
		tree[p].len=w;
		if(left==right) return;
		int mid=(left+right)>>1;
		build(L(p),left,mid);
		build(R(p),mid+1,right);
	}
	void query(int p,int x,int& ans)
	{
		if(tree[p].len<x)
		{
			ans=-1;
			return;
		};
		if(tree[p].left==tree[p].right)
		{
			tree[p].len-=x;
			ans=tree[p].left;
			return;
		}
		if(tree[L(p)].len>=x)
			query(L(p),x,ans); //这个地方等号要等到才行
		else if(tree[R(p)].len>=x) 
			query(R(p),x,ans);
		update(p);
	}
}seg;
int main()
{
	int i,j,k,len,ans;
	while(scanf("%d%d%d",&h,&w,&n)!=EOF)
	{
		len=min(h,n);
		seg.build(1,1,len);
		while(n--)
		{
			scanf("%d",&k);
			ans=-1;
			seg.query(1,k,ans);
			printf("%d\n",ans);
		}
	}
	return 0;
}