一塊 h×w 的廣告牌,依次貼 n 個 1×wᵢ 的廣告。每次貼到當前最高可用行的最左端。求每個廣告貼上後所在行號(1-based),若無法貼上輸出 -1。
每個節點維護:區間內最大可用寬度。葉子節點對應一行,初始值為 w。
void build(int rt, int l, int r) {
tree[rt] = w; // 區間最大可用寬度
if (l == r) return;
int mid = (l+r)/2;
build(rt*2, l, mid);
build(rt*2+1, mid+1, r);
}
// 返回放置行號,同時更新該行剩餘寬度
int query(int rt, int l, int r, int w) {
if (l == r) {
tree[rt] -= w;
return l;
}
int mid = (l+r)/2;
int ans = (tree[rt*2] >= w) ? query(rt*2, l, mid, w)
: query(rt*2+1, mid+1, r, w);
tree[rt] = max(tree[rt*2], tree[rt*2+1]); // 向上更新
return ans;
}
#include
#include
using namespace std;
const int MAXN = 200000;
int tree[MAXN*4];
int h, w, n;
void build(int rt, int l, int r) {
tree[rt] = w;
if (l == r) return;
int mid = (l+r)>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
}
int query(int rt, int l, int r, int x) {
if (l == r) { tree[rt] -= x; return l; }
int mid = (l+r)>>1;
int ans = (tree[rt<<1] >= x) ? query(rt<<1, l, mid, x)
: query(rt<<1|1, mid+1, r, x);
tree[rt] = max(tree[rt<<1], tree[rt<<1|1]);
return ans;
}
int main() {
while (scanf("%d%d%d", &h, &w, &n) == 3) {
if (h > n) h = n; // 優化:行數超過廣告數無意義
build(1, 1, h);
while (n--) {
int q; scanf("%d", &q);
if (tree[1] < q) puts("-1");
else printf("%d\n", query(1, 1, h, q));
}
}
return 0;
}
| 優化點 | 說明 |
|---|---|
| h = min(h, n) | 廣告數 n 較少時,多餘行永遠用不到,減小樹規模防 RE |
| 查詢即更新 | 避免單獨更新函數,查詢遞歸返回時順便 pull 更新 |
| 根節點快速判斷 | tree[1] 為全局最大可用寬度, |
| 靜態數組 | 避免 new/delete 開銷,MAXN=200000 足夠 h≤200000 |
此題展示了線段樹「區間最大值 + 查找第一個滿足條件位置」的經典模式。可擴展到:
HDU 2795 是線段樹入門佳作:模型簡單(單點更新、全局最大查詢)、代碼精簡(查詢更新合一)、優化實用(h=n 剪枝)。掌握此題即掌握了線段樹「查找第一個滿足條件位置」的核心套路。