HDU 2795 Billboard(线段树)

题意:

有一个h*w的公告牌,h是高度,w是宽度,一个单位高度1为一行,然后会有一些公告贴上去,公告是1*wi大小的长纸条,优先贴在最上面并且最左边的位置,如果没有空间贴得下,就输出-1,可以的话,就输出所贴的位置(第几行)。

解析:

叶节点maxv[x]表示board的第x行还可以放置的长度,区间[a,b]表示第a行到b行中剩下空间最大的那一行是多少,如果要把长w的公告放入board时就是update,优先往左子树走(如果左子树的空间足够的话),一直走到叶节点,更新这个叶节点剩下的长度,然后再向上更新。

注意:

由于h最大为10^9,一开始时没注意,建树直接build(1,1,h),而开不了这么大的数组,其实n最大才20W,所以h组多也只需要用到20W,只需要取 h = min(h,n) 即可。

还有这题的数据有变动数据量加大了,所以在输入时我选择了用输入外挂,并在查询时剪枝(如果已经找到答案,直接return)。

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;

int maxv[N << 2];
int len, ans;
int h, w, n;
inline void read(int &x) {
    int flag = 0;
    x = 0;
    char c = getchar();
    if(c == '-')
        flag = 1;
    while(c < '0' || c > '9') {
        if(c == '-')
            flag = 1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
        x = x * 10 + c - '0', c = getchar();
    if(flag) x = -x;
}
void build(int o, int L, int R) {
    if(L == R) {
        maxv[o] = w;
        return ;
    }
    int M = (L + R)/2;
    build(o*2, L, M);
    build(o*2+1, M+1, R);
    maxv[o] = max(maxv[o*2], maxv[o*2+1]);
}
void query(int o, int L, int R) {
    if(L == R && !ans) {
        maxv[o] -= len;
        ans = L;
        return;
    }
    int M = (L+R) / 2;
    if(ans) return;
    if(maxv[o*2] >= len) {
        query(o*2, L, M);
        maxv[o] = max(maxv[o*2], maxv[o*2+1]);
    }else if(maxv[o*2+1] >= len) {
        query(o*2+1, M+1, R);
        maxv[o] = max(maxv[o*2], maxv[o*2+1]);
    }
}
int main() {
    while(scanf("%d%d%d", &h, &w, &n) != EOF) {
        h = min(n, h);
        build(1, 1, h);
        for(int i = 1; i <= n; i++) {
            read(len);
            if(len > maxv[1])
                puts("-1");
            else {
                ans = 0;
                query(1, 1, h);
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

你可能感兴趣的:(HDU,2795)