3161. Block Placement Queries
題目 / Problem
中文
有一條無限長的數線,原點在 0,向正向延伸。給你一個查詢陣列 queries,有兩種查詢:
[1, x]:在距離原點x處放一個障礙物(保證此時x處還沒有障礙物)。[2, x, sz]:問能不能在[0, x]這段範圍內放下一個長度為sz的方塊,方塊必須完全落在[0, x]內。方塊不能與任何障礙物相交,但可以緊貼(碰到)障礙物。注意只是「詢問」,並不會真的放下方塊,每個查詢互相獨立。
回傳一個布林陣列,第 i 個 type-2 查詢能放下就是 true,否則 false。
English
There is an infinite number line starting at origin 0, extending in the positive direction. You are given queries with two types:
[1, x]: place an obstacle at distancex(guaranteed empty there at that moment).[2, x, sz]: can you place a block of lengthszsomewhere inside[0, x]so it lies entirely within[0, x]? The block may not intersect any obstacle but may touch it. You don't actually place it; queries are independent.
Return a boolean array of answers for the type-2 queries.
Constraints
- 1 <= queries.length <= 1.5 * 10^5
- 1 <= x, sz <= min(5 * 10^4, 3 * queries.length)
- For type-1, x is always empty when asked. At least one type-2 query exists.
Worked example
queries = [[1,2],[2,3,3],[2,3,1],[2,2,2]] → [false, true, true]
After placing an obstacle at x = 2, the largest block that fits before x = 3 has size 2, so size 3 fails but sizes 1 and 2 succeed.
名詞解釋 / Glossary
- 障礙物 / obstacle:數線上某個座標被佔住的點。方塊不能蓋過它,但端點可以剛好碰到它。An occupied point; a block may touch but not overlap it.
- 間隙 / gap:兩個相鄰障礙物之間(或從 0 到第一個障礙物之間)的一段空白長度,方塊能塞進去的最大尺寸。The empty span between two consecutive obstacles (or from 0 to the first obstacle).
- 線段樹 / segment tree:一種把陣列建成二元樹的資料結構,支援「單點修改」與「區間查詢(這裡是區間最大值)」,每次操作都是
O(log n)。A binary-tree structure over an array supporting point updates and range queries (here, range maximum) inO(log n). - 點更新 / point update:只改某一個位置的值。Change the value at a single index.
- 區間最大查詢 / range-max query:問某段區間
[l, r]裡的最大值。Ask for the maximum value over an index range. - 離線處理 / offline processing:先把所有查詢讀進來,再用對我們最方便的順序處理,而不是一邊讀一邊答。Read all queries first, then answer them in whatever order is most convenient.
- 逆向處理 / reverse processing:從最後一個查詢往前處理。本題正向是「插入障礙物會把一個大間隙切成兩個」(難維護最大值),逆向則是「移除障礙物會把兩個間隙合併成一個」(好維護)。Walk queries from last to first, turning hard insertions into easy merges.
- 雙向鏈結串列 / doubly linked list:每個元素記住它的「前一個」「後一個」,可以
O(1)找到鄰居並在刪除時重接。Each node knows its previous/next neighbor, so deletion and neighbor lookup areO(1).
思路
先想暴力法。對每個 type-2 查詢 [2, x, sz],我們要判斷 [0, x] 裡是否存在一段長度 >= sz 的空白。最直接的做法是把目前所有障礙物排序,掃過 [0, x] 內每一對相鄰障礙物算間隙,再看有沒有間隙夠大。但查詢數量可達 1.5 * 10^5,每次又要掃過所有障礙物,最壞是 O(Q^2),會超時。
關鍵觀察:能不能放下方塊,只取決於 [0, x] 內的「最大空白」。空白有兩種來源。第一種是兩個相鄰障礙物之間的間隙 [a, b],只要右端點 b <= x,這段就完整落在 [0, x] 內,可容納長度 b - a 的方塊(從 0 到第一個障礙物的那段也算,相當於前一個障礙物在座標 0)。第二種是「尾段空白」:設 x 左邊最近的障礙物在 p,那麼 [p, x] 之間沒有障礙物,可放下 x - p 的方塊;若 x 左邊根本沒有障礙物,整段 [0, x] 都空著,能放 x。把這兩種的最大值拿來和 sz 比較即可。
剩下的問題是怎麼快速維護這些間隙。麻煩在於 type-1 是「插入」障礙物,插入會把一個大間隙切成兩個小間隙——要在資料結構裡同時「減少一個值、增加一個值」並維護最大值並不直觀。於是改用離線逆向:先把所有會出現的障礙物全部放好,從最後一個查詢往前掃,遇到 type-1 就把那個障礙物移除。移除剛好是插入的反操作——它把兩個相鄰間隙合併成一個,用雙向鏈結串列 O(1) 找到左右鄰居即可重接。我們用兩棵線段樹:segGap 在每個障礙物座標 p 存「p 與它左鄰障礙物之間的間隙」,查詢 [1, x] 的區間最大值就得到第一種空白;segPos 在每個障礙物座標 p 存值 p,查詢 [1, x] 的最大值就得到「不超過 x 的最近障礙物座標」,用來算尾段空白。每個 type-2 查詢就是兩次 O(log) 查詢。
Start with brute force. For each type-2 query [2, x, sz] we ask whether [0, x] contains an empty span of length >= sz. The naive way sorts the current obstacles and scans every consecutive pair inside [0, x]. With up to 1.5 * 10^5 queries each scanning all obstacles, that's O(Q^2) — too slow.
The key insight: the answer depends only on the largest empty span inside [0, x]. Empty spans come in two flavors. First, the gap [a, b] between two consecutive obstacles: as long as the right end b <= x, that whole gap lies in [0, x] and fits a block of length b - a (the span from 0 to the first obstacle counts too — treat the "previous obstacle" of the first one as coordinate 0). Second, the trailing span: if the nearest obstacle at or before x is at p, then [p, x] is empty and fits a block of x - p; if no obstacle is <= x, all of [0, x] is free and fits x. Take the max of the two and compare with sz.
The remaining difficulty is maintaining these gaps efficiently. Type-1 is an insertion, and an insertion splits one big gap into two — awkward, because it both decreases and increases values while we track a maximum. So we go offline and reverse: place every obstacle that will ever appear up front, walk queries from last to first, and on a type-1 query remove that obstacle. Removal is the inverse of insertion — it merges two neighboring gaps into one, and with a doubly linked list we find the left/right neighbors in O(1) and relink. Two segment trees do the rest: segGap stores at each obstacle position p the gap between p and its left neighbor (a range-max over [1, x] gives the first kind of span); segPos stores value p at position p (a range-max over [1, x] gives the nearest obstacle <= x, used for the trailing span). Each type-2 query is then two O(log) lookups.
逐步走查 / Walkthrough
用第一個範例 queries = [[1,2],[2,3,3],[2,3,1],[2,2,2]],預期輸出 [false, true, true]。
Using the first sample queries = [[1,2],[2,3,3],[2,3,1],[2,2,2]], expected [false, true, true].
Setup / 初始化
- 所有障礙物 / all obstacles ever placed: {2}(來自 [1,2])。最大座標 maxX = 3。
- 一開始全部放好 / start with all obstacles present: {2}.
- segGap:在座標 2 存 2 - 0 = 2(左邊沒有障礙物,視為座標 0)。segGap at pos 2 = 2.
- segPos:在座標 2 存 2。segPos at pos 2 = 2.
- 三個 type-2 查詢對應輸出槽 / output slots: query1→slot0, query2→slot1, query3→slot2.
逆向處理(從 index 3 到 index 0)/ process in reverse:
| step | query (index) | 動作 / action | segPos.query(1,x) = lastObs |
trailing = x - lastObs |
segGap.query(1,x) = internalMax |
best | best ≥ sz ? | 寫入 / write |
|---|---|---|---|---|---|---|---|---|
| 1 | [2,2,2] (i=3) |
查詢 / query, x=2, sz=2 | 2 |
2-2=0 |
2 |
2 |
2≥2 → true |
slot2 = true |
| 2 | [2,3,1] (i=2) |
查詢 / query, x=3, sz=1 | 2 |
3-2=1 |
2 |
2 |
2≥1 → true |
slot1 = true |
| 3 | [2,3,3] (i=1) |
查詢 / query, x=3, sz=3 | 2 |
3-2=1 |
2 |
2 |
2≥3 → false |
slot0 = false |
| 4 | [1,2] (i=0) |
移除座標 2 / remove obstacle 2:segGap[2]=0, segPos[2]=0 |
— | — | — | — | — | — |
最後輸出槽 [slot0, slot1, slot2] = [false, true, true]。✓
Final slots [false, true, true]. ✓
注意 step 1 為什麼 [2,2,2] 是 true:障礙物在座標 2,間隙 [0, 2] 長度 2,方塊從 0 放到 2 剛好碰到障礙物(碰到允許),所以放得下。
Why step 1 is true: the gap [0, 2] has length 2; a block from 0 to 2 just touches the obstacle (touching is allowed), so it fits.
Solution — C
#include <stdlib.h>
#include <stdbool.h>
/*
* 演算法 / Algorithm:
* 離線 + 逆向掃描。先把所有障礙物放好,由後往前處理;遇到 type-1 就「移除」障礙物,
* 這樣較早的查詢看到的就是正確的障礙物集合。移除會把兩個相鄰間隙合併成一個。
* 兩棵線段樹:segGap 維護相鄰障礙物間的最大間隙,segPos 維護不超過 x 的最大障礙物座標。
* Offline + reverse scan. Place all obstacles, walk queries backwards, "remove" an
* obstacle on its type-1 query (merging two gaps). segGap tracks max gap between
* consecutive obstacles; segPos finds the largest obstacle position <= x.
*/
/* 給 qsort 用的整數比較器 / integer comparator for qsort */
static int cmpInt(const void *a, const void *b) {
int x = *(const int *)a, y = *(const int *)b; // 解參考取出兩個 int / dereference both ints
return (x > y) - (x < y); // 回傳 -1/0/1,避免相減溢位 / safe compare, no overflow
}
/* 線段樹:點更新 + 區間最大值 / point-assign + range-max segment tree */
typedef struct {
int *tree; // 隱式二元樹陣列(1-indexed)/ implicit binary tree array (1-indexed)
int n; // 葉子數,代表座標 1..n / number of leaves = positions 1..n
} Seg;
/* 建立一棵全為 0 的線段樹 / create a segment tree filled with zeros */
static Seg *segCreate(int n) {
Seg *s = (Seg *)malloc(sizeof(Seg)); // 配置結構本身 / allocate the struct
s->n = n; // 記住葉子數 / store leaf count
s->tree = (int *)calloc(4 * (n + 1), sizeof(int)); // calloc 會把記憶體清成 0 / calloc zeroes memory; 4*(n+1) is safe size
return s;
}
/* 把座標 pos 的值設成 val / set the value at position pos to val */
static void segUpdate(Seg *s, int node, int lo, int hi, int pos, int val) {
if (lo == hi) { // 走到葉子節點 / reached the leaf
s->tree[node] = val; // 直接賦值 / assign the new value
return;
}
int mid = lo + (hi - lo) / 2; // 取中點,避免溢位 / midpoint without overflow
if (pos <= mid) segUpdate(s, 2 * node, lo, mid, pos, val); // 往左子樹 / go left
else segUpdate(s, 2 * node + 1, mid + 1, hi, pos, val); // 往右子樹 / go right
int a = s->tree[2 * node], b = s->tree[2 * node + 1]; // 取兩個子節點 / two children
s->tree[node] = a > b ? a : b; // 父節點存兩者較大值 / parent keeps the max
}
/* 查詢區間 [ql, qr] 的最大值 / query the maximum over [ql, qr] */
static int segQuery(Seg *s, int node, int lo, int hi, int ql, int qr) {
if (qr < lo || hi < ql) return 0; // 完全沒交集,回傳 0(間隙與座標都 >=0)/ no overlap -> 0
if (ql <= lo && hi <= qr) return s->tree[node]; // 區間被完全覆蓋,直接回傳 / fully covered
int mid = lo + (hi - lo) / 2; // 中點 / midpoint
int a = segQuery(s, 2 * node, lo, mid, ql, qr); // 查左半 / query left half
int b = segQuery(s, 2 * node + 1, mid + 1, hi, ql, qr);// 查右半 / query right half
return a > b ? a : b; // 合併取最大 / combine by max
}
bool *getResults(int **queries, int queriesSize, int *queriesColSize, int *returnSize) {
/* 1) 掃一遍:找最大座標 maxX、數出障礙物個數 / first pass: find maxX and count obstacles */
int maxX = 1, obsCount = 0;
for (int i = 0; i < queriesSize; i++) {
if (queries[i][1] > maxX) maxX = queries[i][1]; // queries[i][1] 是 x(兩種查詢都有)/ x exists in both types
if (queries[i][0] == 1) obsCount++; // type-1 才放障礙物 / only type-1 places obstacles
}
/* 2) 收集所有障礙物座標並排序 / collect obstacle positions and sort */
int *obs = (int *)malloc(sizeof(int) * (obsCount > 0 ? obsCount : 1)); // 至少配 1 格避免 0 大小 / at least size 1
int k = 0;
for (int i = 0; i < queriesSize; i++)
if (queries[i][0] == 1) obs[k++] = queries[i][1]; // 依出現順序先放進去 / gather then sort
qsort(obs, obsCount, sizeof(int), cmpInt); // 由小到大排序 / sort ascending
/* 3) 座標 -> 排序後索引,方便移除時找鄰居 / map position to its sorted index */
int *pos2idx = (int *)malloc(sizeof(int) * (maxX + 1));
for (int i = 0; i <= maxX; i++) pos2idx[i] = -1; // -1 表示該座標沒障礙物 / -1 means no obstacle
for (int i = 0; i < obsCount; i++) pos2idx[obs[i]] = i;
/* 4) 雙向鏈結串列:記住每個障礙物的左右鄰居 / doubly linked list of obstacles */
int m = obsCount;
int *prevArr = (int *)malloc(sizeof(int) * (m + 1)); // 多配一格給右哨兵 / extra slot for right sentinel
int *nextArr = (int *)malloc(sizeof(int) * (m + 1));
for (int i = 0; i < m; i++) { prevArr[i] = i - 1; nextArr[i] = i + 1; }
/* prevArr[0] = -1 代表左邊沒有障礙物(視為座標 0)/ -1 = no left obstacle (acts as coord 0) */
/* nextArr[m-1] = m 代表右邊沒有障礙物 / m = no right obstacle */
/* 5) 建兩棵線段樹,並把「全部障礙物都在」的初始狀態填進去 / build trees for the all-present state */
Seg *segGap = segCreate(maxX); // 每個障礙物存它與左鄰的間隙 / gap to left neighbor
Seg *segPos = segCreate(maxX); // 每個障礙物存自己的座標 / its own position
for (int i = 0; i < m; i++) {
int gapLeft = obs[i] - (i > 0 ? obs[i - 1] : 0); // 左鄰不存在就用 0 / left neighbor defaults to 0
segUpdate(segGap, 1, 1, maxX, obs[i], gapLeft);
segUpdate(segPos, 1, 1, maxX, obs[i], obs[i]);
}
/* 6) 算 type-2 數量、配置答案、預先決定每個查詢的輸出位置 / prepare output slots */
int q2 = 0;
for (int i = 0; i < queriesSize; i++) if (queries[i][0] == 2) q2++;
bool *ans = (bool *)malloc(sizeof(bool) * (q2 > 0 ? q2 : 1));
*returnSize = q2; // 透過指標回傳答案長度 / report length via pointer
int *outSlot = (int *)malloc(sizeof(int) * queriesSize);
int s = 0;
for (int i = 0; i < queriesSize; i++) outSlot[i] = (queries[i][0] == 2) ? s++ : -1; // 第幾個 type-2 / its rank
/* 7) 逆向掃描所有查詢 / scan all queries in reverse */
for (int i = queriesSize - 1; i >= 0; i--) {
if (queries[i][0] == 1) {
/* --- 移除障礙物(插入的逆操作)/ remove an obstacle (inverse of insert) --- */
int x = queries[i][1];
int idx = pos2idx[x]; // 它在排序陣列中的索引 / its sorted index
int l = prevArr[idx], r = nextArr[idx]; // 左右鄰居 / left & right neighbors
segUpdate(segGap, 1, 1, maxX, x, 0); // 它的間隙消失 / its gap disappears
segUpdate(segPos, 1, 1, maxX, x, 0); // 它不再是障礙物 / no longer an obstacle
if (l >= 0) nextArr[l] = r; // 重接鏈結串列 / relink left -> right
if (r < m) prevArr[r] = l; // 重接鏈結串列 / relink right -> left
if (r < m) { // 右鄰存在,它的間隙要擴大 / right neighbor's gap grows
int prevPos = (l >= 0) ? obs[l] : 0; // 合併後右鄰的新左界 / merged gap's new left bound
segUpdate(segGap, 1, 1, maxX, obs[r], obs[r] - prevPos);
}
} else {
/* --- 回答 type-2 查詢 / answer a type-2 query --- */
int x = queries[i][1], sz = queries[i][2];
int lastObs = segQuery(segPos, 1, 1, maxX, 1, x); // 不超過 x 的最近障礙物(無則 0)/ nearest obstacle <= x
int trailing = x - lastObs; // 尾段空白:從該障礙物到 x / trailing free span
int internalMax = segQuery(segGap, 1, 1, maxX, 1, x); // 內部最大間隙 / max internal gap with right end <= x
int best = trailing > internalMax ? trailing : internalMax; // 兩種空白取較大 / largest empty span
ans[outSlot[i]] = (best >= sz); // 夠大就放得下 / fits iff best >= sz
}
}
/* 8) 釋放暫存記憶體(ans 要回傳,不能 free)/ free scratch memory (keep ans) */
free(obs); free(pos2idx); free(prevArr); free(nextArr); free(outSlot);
free(segGap->tree); free(segGap);
free(segPos->tree); free(segPos);
return ans;
}
Solution — C++
#include <vector>
#include <algorithm>
using namespace std;
/*
* 演算法 / Algorithm:
* 離線 + 逆向掃描。先放好所有障礙物,由後往前處理,type-1 視為「移除」(合併兩個間隙)。
* segGap 維護相鄰障礙物間的最大間隙,segPos 找出不超過 x 的最大障礙物座標。
* Offline + reverse scan: place all obstacles, walk backwards, treat type-1 as removal
* (merging two gaps). segGap = max gap between consecutive obstacles; segPos = largest
* obstacle position <= x. Each type-2 query is two O(log) lookups.
*/
class Solution {
// 線段樹:點更新 + 區間最大值 / point-assign + range-max segment tree
struct Seg {
int n; // 葉子數 / number of leaves
vector<int> tree; // 隱式樹,vector 會自動管理記憶體 / implicit tree; vector manages memory
Seg(int n) : n(n), tree(4 * (n + 1), 0) {} // 建構:開 4*(n+1) 個 0 / build with zeros
// 內部遞迴版點更新 / internal recursive point update
void update(int node, int lo, int hi, int pos, int val) {
if (lo == hi) { tree[node] = val; return; } // 葉子直接賦值 / assign at leaf
int mid = lo + (hi - lo) / 2; // 中點 / midpoint
if (pos <= mid) update(2 * node, lo, mid, pos, val); // 往左 / go left
else update(2 * node + 1, mid + 1, hi, pos, val);// 往右 / go right
tree[node] = max(tree[2 * node], tree[2 * node + 1]); // 父=子的最大 / parent = max of children
}
void update(int pos, int val) { update(1, 1, n, pos, val); } // 對外簡化介面 / public wrapper
// 內部遞迴版區間最大查詢 / internal recursive range-max query
int query(int node, int lo, int hi, int ql, int qr) {
if (qr < lo || hi < ql) return 0; // 無交集 / no overlap
if (ql <= lo && hi <= qr) return tree[node]; // 完全覆蓋 / fully covered
int mid = lo + (hi - lo) / 2;
return max(query(2 * node, lo, mid, ql, qr),
query(2 * node + 1, mid + 1, hi, ql, qr)); // 左右取最大 / combine by max
}
int query(int ql, int qr) { return query(1, 1, n, ql, qr); } // 對外簡化介面 / public wrapper
};
public:
vector<bool> getResults(vector<vector<int>>& queries) {
int n = queries.size();
int maxX = 1;
vector<int> obs; // 所有障礙物座標 / all obstacle positions
for (auto& q : queries) { // range-for:逐一取出每個查詢 / iterate each query
maxX = max(maxX, q[1]); // q[1] 是 x / x for both types
if (q[0] == 1) obs.push_back(q[1]); // type-1 收集障礙物 / collect obstacles
}
sort(obs.begin(), obs.end()); // 由小到大排序 / sort ascending
int m = obs.size();
vector<int> pos2idx(maxX + 1, -1); // 座標 -> 排序索引,-1 表示無 / position -> sorted index
for (int i = 0; i < m; i++) pos2idx[obs[i]] = i;
vector<int> prevArr(m + 1), nextArr(m + 1); // 雙向鏈結串列 / doubly linked list
for (int i = 0; i < m; i++) { prevArr[i] = i - 1; nextArr[i] = i + 1; }
// prevArr[0] = -1:左邊視為座標 0;nextArr[m-1] = m:右邊無障礙物
// prevArr[0] = -1 acts as coord 0; nextArr[m-1] = m means no right obstacle
Seg segGap(maxX), segPos(maxX); // 兩棵線段樹 / two segment trees
for (int i = 0; i < m; i++) {
int gapLeft = obs[i] - (i > 0 ? obs[i - 1] : 0); // 與左鄰的間隙,無左鄰用 0 / gap to left neighbor
segGap.update(obs[i], gapLeft);
segPos.update(obs[i], obs[i]);
}
vector<int> outSlot(n, -1); // 每個查詢的輸出位置 / output slot per query
int q2 = 0;
for (int i = 0; i < n; i++) if (queries[i][0] == 2) outSlot[i] = q2++;
vector<bool> ans(q2); // 答案陣列 / answers
for (int i = n - 1; i >= 0; i--) { // 逆向掃描 / reverse scan
if (queries[i][0] == 1) {
// --- 移除障礙物 / remove obstacle ---
int x = queries[i][1], idx = pos2idx[x];
int l = prevArr[idx], r = nextArr[idx]; // 左右鄰居 / neighbors
segGap.update(x, 0); // 間隙消失 / gap gone
segPos.update(x, 0); // 不再是障礙物 / not an obstacle
if (l >= 0) nextArr[l] = r; // 重接 / relink
if (r < m) prevArr[r] = l; // 重接 / relink
if (r < m) { // 右鄰間隙擴大 / right neighbor's gap grows
int prevPos = (l >= 0) ? obs[l] : 0; // 合併後的左界 / merged left bound
segGap.update(obs[r], obs[r] - prevPos);
}
} else {
// --- 回答 type-2 / answer type-2 ---
int x = queries[i][1], sz = queries[i][2];
int lastObs = segPos.query(1, x); // 不超過 x 的最近障礙物 / nearest obstacle <= x
int trailing = x - lastObs; // 尾段空白 / trailing span
int internalMax = segGap.query(1, x); // 內部最大間隙 / max internal gap (right end <= x)
ans[outSlot[i]] = max(trailing, internalMax) >= sz; // 取較大者與 sz 比 / fits iff best >= sz
}
}
return ans;
}
};
複雜度 / Complexity
- Time:
O((Q + V) log V)—Q是查詢數,V是最大座標(maxX,最多5 * 10^4)。建樹要對每個障礙物做一次O(log V)更新;之後每個查詢做常數次O(log V)的線段樹更新或查詢。排序障礙物是O(m log m),被前者吸收。Qqueries each costO(log V)for the segment-tree operations; building the trees over the obstacles isO(m log V). Thelogfactor is the height of the segment tree. - Space:
O(V)— 兩棵線段樹各約4V個整數,加上pos2idx、鏈結串列等也都是O(V)或O(Q)。Two segment trees of size~4V, plusO(V)/O(Q)helper arrays.
Pitfalls & Edge Cases
- 「碰到」不算相交 / touching is allowed:間隙
[a, b]能容納長度b - a(不是b - a - 1)的方塊,因為端點可緊貼障礙物。判斷用best >= sz而非>。Use>=, and the gap length isb - a. - 第一段與尾段空白別漏掉 / don't forget the first and trailing spans:從 0 到第一個障礙物(左鄰視為座標 0)以及從最後一個
<= x的障礙物到x都是合法空白。程式分別用「左鄰預設 0」和segPos查詢來處理。Handle them via the "left neighbor defaults to 0" rule and thesegPoslookup. - 正向插入難、逆向刪除易 / insert is hard, delete is easy:若正向處理,插入會把一個間隙拆成兩個,難維護最大值;逆向把它變成合併,鏈結串列
O(1)搞定。這是本題用離線逆向的核心理由。This is the whole reason for going offline-and-reverse. - 線段樹陣列大小 / segment-tree array size:要開
4*(n+1),開太小會越界寫入造成難以察覺的錯誤。Under-sizing causes out-of-bounds writes. maxX要涵蓋所有 x /maxXmust cover every x:type-2 的x也可能比任何障礙物座標大,必須一起納入maxX,否則查詢[1, x]會越界。Include type-2xvalues when computingmaxX.- 沒有障礙物的情況 / no obstacles at all:若某查詢時
[0, x]沒障礙物,segPos查回0,trailing = x,整段空白可放x,邏輯自然成立。Empty-range case falls out correctly:trailing = x. - 輸出順序 / output ordering:逆向處理時答案是亂序產生的,務必用預先算好的
outSlot寫回正確位置,回傳的順序要對應 type-2 查詢的原始先後。Write answers back viaoutSlot, not in processing order.