题目大意:给定一个h*w的宣传板,然后在给定n个1*wi的小广告,问这些小广告在宣传板的第几行(因为每个小广告都想放在最上面,且最靠左边的位置)。
题目分析:刚开始想到的是将整个宣传板看做时一个1~h*w的区间,然后用1*wi的小广告来按照题意来更新这段区间。但是这种想法很天真。操作性压根都没用。所以不妨换一种思路。将宣传版的每一行都看做是一个点,这样整个宣传板就可以看做是一个1~h的区间,然后每个叶子节点维护的是当前行还剩下的长度。父亲节点维护的是子节点剩下的最长的长度(这样可以减枝)。这样就可以用线段树解决问题了。将update放在query中进行。注意,最多有20w个传单,也就是最多会占用20w行的宣传版。发现只要想的大致方向对,题目都会隐藏的对你说如何做。这就是刘未鹏说的好题吧。
下面是代码:
/*Hdu 2795 Billboard
segment tree
*/
#include <stdio.h>
#include <algorithm>
using namespace std;
#define maxn 210000
int re[maxn<<2];
int h,w,n;
void push_up(int rt)
{
re[rt] = max(re[rt<<1],re[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l == r)
{
re[rt] = w;
return ;
}
int mid = (l + r) >> 1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
int query(int v,int l,int r,int rt)
{
if(v > re[rt]) return -1;
if(l == r)
{
re[rt] -= v;
return l;
}
int mid = (l + r) >> 1;
int ret = -1;
if(v <= re[rt<<1]) ret = query(v,l,mid,rt<<1);
else if(v <= re[rt<<1|1]) ret = query(v,mid+1,r,rt<<1|1);
push_up(rt);
return ret;
}
int main()
{
while(~scanf("%d%d%d",&h,&w,&n))
{
build(1,min(h,n),1);
for(int i = 0; i < n; i++)
{
int x;
scanf("%d",&x);
int ans = query(x,1,min(n,h),1);
printf("%d\n",ans);
}
}
return 0;
}