有一塊高 h、寬 w 的宣傳欄。有 n 條公告,每條高 1、寬 lenᵢ。公告必須貼在「最上面、最靠左」的可用位置。判斷每條公告能否貼上,能則輸出其左上角行號(1-based),否則輸出 -1。
原問題是「找最上最左」,等價於將宣傳欄上下翻轉後變為「找最左、且行號最小(即原最上)的可放置行」。此時每行剩餘寬度初始為 w,放入公告後剩餘寬度減少。只需在 h 行中找第一個剩餘寬度 ≥ lenᵢ 的行。
wlen:若根節點最大值 < len 則無解;否則遞歸優先進入左子樹(保證最左),找到葉子後更新剩餘寬度並向上修正最大值min(h, n)因為最多只會用到 n 行(每條公告佔一行),若 n 只需建 n 行的樹,多餘行永遠不會被使用。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define lson(x) ((x) << 1)
#define rson(x) ((x) << 1 | 1)
typedef long long ll;
const int N = 200010;
const int ROOT = 1;
struct SegNode { ll mx; };
struct SegTree {
SegNode node[N << 2];
void update(int pos) { node[pos].mx = max(node[lson(pos)].mx, node[rson(pos)].mx); }
void build(int l, int r, int pos) {
if (l == r) { node[pos].mx = w; return; }
int m = (l + r) >> 1;
build(l, m, lson(pos));
build(m+1, r, rson(pos));
update(pos);
}
// 返回能放下 y 的最左行號,或 -1
int query_modify(int l, int r, int pos, ll y) {
if (y > node[pos].mx) return -1;
if (l == r) { node[pos].mx -= y; return l; }
int m = (l + r) >> 1;
int res = (y <= node[lson(pos)].mx) ?
query_modify(l, m, lson(pos), y) :
query_modify(m+1, r, rson(pos), y);
update(pos);
return res;
}
} seg;
int h, w, n, t;
int main() {
while (~scanf("%d%d%d", &h, &w, &n)) {
h = min(h, n); // 關鍵優化
seg.build(1, h, ROOT);
repf(i, 1, n) {
scanf("%d", &t);
printf("%d\n", seg.query_modify(1, h, ROOT, t));
}
}
return 0;
}
O(h'),h' = min(h, n)O(log h')O((h' + n) log h')O(h')min(h, n) 避免 MLE