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
Sample Output
【题意】: 给你一块 w*h 的板,然后让你用一些 1*x 的木块去填,每次填的时候总是选择行靠前的,假设能够放在该行上,则会选择列靠前的,如今让你对每一种木块 输出它所放在的行,假设不能放上去则输出 -1
【分析】: 这题一開始看到数据范围是 10^9 吓了一跳,只是细致分析能够发现,事实上真正实用的仅仅是 min(h,n) 其他都是浪费的。那么好了,就能够往下做了 非常明显的是一个区间染色问题。也非常easy的就想到能够用线段树来搞 只是这题是分成若干段后的染色,一開始可能会认为可能须要建立多个线段树来针对每一段分别来搞 实际上,这题仅仅须要一个线段树就能够搞定了 建一棵 [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;
}