http://acm.hdu.edu.cn/showproblem.php?pid=2795
题意,一块宽w 高h的板,贴上若干宽为1的条,优先一行一行贴,贴不开换下行,问每个条所在的行数。
使用线段树,根节点表示1-h,每个节点储存的是最大值,初始每个节点都没贴,也就是w,当找到叶子节点,且有足够大的空间贴广告时,改变域值,返回。如果当前区间的最大可用空间都不够用时,即可return。如果已经找到合适的位置了,右半区间就不用再找了。每次回溯的时候,要把改变过的最大空间值MAX,层层返回,层层更新。注意到h与w的范围可以达到10的9次方,如果以h的范围来建树应该会爆内存。但是n的最大值不过是20万而已,分析一下可知n个公告最多占用布告板的高度为n。所以应该可以用n的范围来开内存,然后以min(n, h)的值作为区间范围来建树。节点存储高度编号区间的剩余可用宽度最大值就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=200005;
int h,w,n;
int MAX[maxn<<2];
void build(int l,int r,int rt)
{
MAX[rt]=w;
if(l==r)
return ;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
int query(int x,int l,int r,int rt)
{
if(l==r)
{
MAX[rt]-=x;
return l;
}
int m=(l+r)>>1;
int ret=0;
if(x<=MAX[rt<<1]) ret=query(x,l,m,rt<<1);
else ret=query(x,m+1,r,rt<<1|1);
MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
return ret;
}
int main()
{
while(scanf("%d%d%d",&h,&w,&n)!=-1)
{
if(h>n) h=n;
build(1,n,1);
while(n--)
{
int t;
scanf("%d",&t);
if(t>MAX[1])
printf("-1\n");
else
printf("%d\n",query(t,1,h,1));
}
}
return 0;
}