题意:
有一个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;
}