关键在于建树更新,能想到如何建树如何查找的话,这就是一道水题,想不到的话就是一道难题。一共最多有n个宣传板,所以我们可以建一个线段树,它的叶子节点依次从1到n;这样每次查找能否放下的时候,从左儿子开始查找,看左儿子所在区间的最大值是否能放下当前的木板,能放下的话更新放下后区间的值,不能放下的话从右儿子查找,递归下去就可以了;
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define N 200010
#define INF 999999999
using namespace std;
struct tree
{
int l,r,Max;
};
tree p[N*4];
int w,ans;
void build(int i,int l,int r)
{
p[i].l=l;
p[i].r=r;
p[i].Max=w;//建树时顺便将每个结点的值赋为总木板的宽度。
int mid=(l+r)/2;
if(l!=r)
{
build(i*2,l,mid);
build(i*2+1,mid+1,r);
}
}
void insert(int i,int k)
{
if(p[i].l==p[i].r)
{
p[i].Max-=k;//放下木板后需要更新一下现在剩余的位置;
ans=p[i].l;
return ;
}
if(k<=p[i*2].Max)//如果当前要插入的木板的长度小于当前结点左儿子所指的区间的最大值,那么说明左儿子这个区间里是有某一个位置可以放得下当前要插入的木板,进去查找这个位置;
insert(i*2,k);
else
insert(i*2+1,k);//因为要求是从最上面开始放,所以相应的应该先查找线段树的左子树,查找完不存在的话再查找右子树;
p[i].Max=max(p[i*2].Max,p[i*2+1].Max);//递归结束后返回上一级的时候,顺带着将左右儿子中的最大值返回给父亲结点并更新,方便下次左右递归查找时直接锁定该区间的最大值;
}
int main()
{
int h,n,wi,i;
while(~scanf("%d%d%d",&h,&w,&n))
{
if(h>n)//不能以h的值来建树,h的值太大,建树建不来,题中说了最多n个小木板,所以应当以n的值来建树;
h=n;
build(1,1,h);
for(i=1;i<=n;i++)
{
ans=-1;
scanf("%d",&wi);
if(p[1].Max>=wi)//p[1].max中存储的是总区间(即从1到n这个大区间)里的最大值(这个区间的最大值每次插入木板后都会再次更新的,更新步骤在insert函数的最后一句),只有在最大值大于等于当前要放的木板的值得时候才可以进行查找位置,因为如果最大值都小于当前要放的木板的值的话,那么从1到n是没有位置可以放得下木板的
insert(1,wi);
printf("%d\n",ans);
}
}
return 0;
}