HDU2795 Billboard
题意:有一块h*w的广告牌,往上面贴n个1*wi的广告(不可覆盖),尽量贴最上面,满足最上面时尽量贴最左边,如果哪个广告贴不下不能就输出-1,贴的下就输出它在哪一行
思路:如果以行数为区间,建立线段树,广告最多有n=200000条,即使每个广告一行也就需要h=200000行就足够了;树的叶子节点表示广告牌的高度,而父节点存储子节点的最大剩余值;所以每次找到最大值的位置(位于叶子节点)然后减去wi
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
int left,right;
int len;
}tree[200001*4];//广告即使每个占一行,最多也就200000行,所以h最大200000就够了
int w,v;//广告牌的宽及广告的宽
void Build(int l,int r,int cur){
tree[cur].left=l;
tree[cur].right=r;
tree[cur].len=w;//叶子节点放入牌子的宽度,其他节点放入左右儿子节点的最
//大值,刚开始叶子都一样所以最大值也就是广告牌的宽度
if(l==r)return ;
int mid=(l+r)/2;
Build(l,mid,cur*2);
Build(mid+1,r,cur*2+1);
}
int Query(int cur){//查询和更新结合在一起
if(tree[cur].left==tree[cur].right){
tree[cur].len-=v;//贴成功
return tree[cur].left;//返回所在的行
}
int ret;
if(tree[cur*2].len>=v)ret=Query(cur*2);//一旦左儿子的len>=v则往左儿子找
else ret=Query(cur*2+1);//左儿子不行才找右儿子,这样保证了topmost
tree[cur].len=max(tree[cur*2].len,tree[cur*2+1].len);//先更新父节点的len值
return ret;//后返回到上一层
}
int main(){
int h,n;//广告牌的高及广告个数
while(scanf("%d%d%d",&h,&w,&n)!=EOF){
if(h>n)h=n;//广告即使每个占一行,最多也就n行,所以当h>n时取n即可
Build(1,h,1);
while(n--){
scanf("%d",&v);//广告的宽
if(tree[1].len<v)printf("-1\n");//根节点存的是所有叶子节点存的最大值
else printf("%d\n",Query(1));
}
}
return 0;
}