HDU2795 贴广告

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;
}

你可能感兴趣的:(HDU2795 贴广告)