Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
There exists an infinite number line, with its origin at 0 and extending towards the positive x-axis.
You are given a 2D array queries, which contains two types of queries:
queries[i] = [1, x]. Build an obstacle at distance x from the origin. It is guaranteed that there is no obstacle at distance x when the query is asked.queries[i] = [2, x, sz]. Check if it is possible to place a block of size sz anywhere in the range [0, x] on the line, such that the block entirely lies in the range [0, x]. A block cannot be placed if it intersects with any obstacle, but it may touch it. Note that you do not actually place the block. Queries are separate.Return a boolean array results, where results[i] is true if you can place the block specified in the ith query of type 2, and false otherwise.
Example 1:
Input: queries = [[1,2],[2,3,3],[2,3,1],[2,2,2]]
Output: [false,true,true]
Explanation:
For query 0, place an obstacle at x = 2. A block of size at most 2 can be placed before x = 3.
Example 2:
Input: queries = [[1,7],[2,7,6],[1,2],[2,7,5],[2,7,6]]
Output: [true,true,false]
Explanation:
x = 7 for query 0. A block of size at most 7 can be placed before x = 7.x = 2 for query 2. Now, a block of size at most 5 can be placed before x = 7, and a block of size at most 2 before x = 2.Constraints:
1 <= queries.length <= 15 * 1042 <= queries[i].length <= 31 <= queries[i][0] <= 21 <= x, sz <= min(5 * 104, 3 * queries.length)x when the query is asked.Problem summary: There exists an infinite number line, with its origin at 0 and extending towards the positive x-axis. You are given a 2D array queries, which contains two types of queries: For a query of type 1, queries[i] = [1, x]. Build an obstacle at distance x from the origin. It is guaranteed that there is no obstacle at distance x when the query is asked. For a query of type 2, queries[i] = [2, x, sz]. Check if it is possible to place a block of size sz anywhere in the range [0, x] on the line, such that the block entirely lies in the range [0, x]. A block cannot be placed if it intersects with any obstacle, but it may touch it. Note that you do not actually place the block. Queries are separate. Return a boolean array results, where results[i] is true if you can place the block specified in the ith query of type 2, and false otherwise.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Segment Tree
[[1,2],[2,3,3],[2,3,1],[2,2,2]]
[[1,7],[2,7,6],[1,2],[2,7,5],[2,7,6]]
building-boxes)fruits-into-baskets-iii)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3161: Block Placement Queries
class FenwickTree {
public FenwickTree(int n) {
vals = new int[n + 1];
}
public void add(int i, int val) {
while (i < vals.length) {
vals[i] = Math.max(vals[i], val);
i += lowbit(i);
}
}
public int get(int i) {
int res = 0;
while (i > 0) {
res = Math.max(res, vals[i]);
i -= lowbit(i);
}
return res;
}
private int[] vals;
private static int lowbit(int i) {
return i & -i;
}
}
class Solution {
public List<Boolean> getResults(int[][] queries) {
final int n = Math.min(50000, queries.length * 3);
List<Boolean> ans = new ArrayList<>();
FenwickTree tree = new FenwickTree(n + 1);
TreeSet<Integer> obstacles = new TreeSet<>(Arrays.asList(0, n)); // sentinel values
for (int[] query : queries) {
final int type = query[0];
if (type == 1) {
final int x = query[1];
obstacles.add(x);
}
}
Iterator<Integer> it = obstacles.iterator();
int x1 = it.next();
while (it.hasNext()) {
final int x2 = it.next();
tree.add(x2, x2 - x1);
x1 = x2;
}
for (int i = queries.length - 1; i >= 0; --i) {
final int type = queries[i][0];
final int x = queries[i][1];
if (type == 1) {
final Integer next = obstacles.higher(x);
final Integer prev = obstacles.lower(x);
if (next != null)
tree.add(next, next - prev);
obstacles.remove(x);
} else {
final int sz = queries[i][2];
final int prev = obstacles.floor(x);
ans.add(tree.get(prev) >= sz || x - prev >= sz);
}
}
Collections.reverse(ans);
return ans;
}
}
// Accepted solution for LeetCode #3161: Block Placement Queries
package main
import (
"github.com/emirpasic/gods/trees/redblacktree"
"math/bits"
"slices"
)
// https://space.bilibili.com/206214
type fenwick []int
func (f fenwick) update(i, val int) {
for ; i < len(f); i += i & -i {
f[i] = max(f[i], val)
}
}
func (f fenwick) preMax(i int) (res int) {
for ; i > 0; i &= i - 1 {
res = max(res, f[i])
}
return res
}
type delUf struct {
left []int
right []int
}
func newDelUf(n int) delUf {
left := make([]int, n+1)
right := make([]int, n+1)
for i := range left {
left[i] = i
right[i] = i
}
return delUf{left, right}
}
func (f delUf) find(fa []int, x int) int {
if fa[x] != x {
fa[x] = f.find(fa, fa[x])
}
return fa[x]
}
func (f delUf) delete(x int) {
if f.find(f.left, x) != x { // x 已经被删除
return
}
f.left[x] = x - 1
f.right[x] = x + 1
}
func (f delUf) prev(x int) int {
return f.find(f.left, x-1)
}
func (f delUf) next(x int) int {
return f.find(f.right, x+1)
}
func getResults(queries [][]int) (ans []bool) {
m := 0
pos := []int{0}
for _, q := range queries {
m = max(m, q[1])
if q[0] == 1 {
pos = append(pos, q[1])
}
}
m++
uf := newDelUf(m)
t := make(fenwick, m)
slices.Sort(pos)
for i := 1; i < len(pos); i++ {
p, q := pos[i-1], pos[i]
t.update(q, q-p)
for j := p + 1; j < q; j++ {
uf.delete(j)
}
}
for j := pos[len(pos)-1] + 1; j < m; j++ {
uf.delete(j)
}
for i := len(queries) - 1; i >= 0; i-- {
q := queries[i]
x := q[1]
pre := uf.prev(x) // x 左侧最近障碍物的位置
if q[0] == 1 {
uf.delete(x)
nxt := uf.next(x) // x 右侧最近障碍物的位置
t.update(nxt, nxt-pre) // 更新 d[nxt] = nxt - pre
} else {
// 最大长度要么是 [0,pre] 中的最大 d,要么是 [pre,x] 这一段的长度
maxGap := max(t.preMax(pre), x-pre)
ans = append(ans, maxGap >= q[2])
}
}
slices.Reverse(ans)
return
}
//
type seg []int
// 把 i 处的值改成 val
func (t seg) update(o, l, r, i, val int) {
if l == r {
t[o] = val
return
}
m := (l + r) >> 1
if i <= m {
t.update(o<<1, l, m, i, val)
} else {
t.update(o<<1|1, m+1, r, i, val)
}
t[o] = max(t[o<<1], t[o<<1|1])
}
// 查询 [0,R] 中的最大值
func (t seg) query(o, l, r, R int) int {
if r <= R {
return t[o]
}
m := (l + r) >> 1
if R <= m {
return t.query(o<<1, l, m, R)
}
return max(t[o<<1], t.query(o<<1|1, m+1, r, R))
}
func getResults2(queries [][]int) (ans []bool) {
m := 0
for _, q := range queries {
if q[0] == 1 {
m = max(m, q[1])
}
}
m++
set := redblacktree.New[int, struct{}]()
set.Put(0, struct{}{}) // 哨兵
set.Put(m, struct{}{})
t := make(seg, 2<<bits.Len(uint(m)))
for _, q := range queries {
x := q[1]
pre, _ := set.Floor(x - 1) // x 左侧最近障碍物的位置
if q[0] == 1 {
nxt, _ := set.Ceiling(x) // x 右侧最近障碍物的位置
set.Put(x, struct{}{})
t.update(1, 0, m, x, x-pre.Key) // 更新 d[x] = x - pre
t.update(1, 0, m, nxt.Key, nxt.Key-x) // 更新 d[nxt] = nxt - x
} else {
// 最大长度要么是 [0,pre] 中的最大 d,要么是 [pre,x] 这一段的长度
maxGap := max(t.query(1, 0, m, pre.Key), x-pre.Key)
ans = append(ans, maxGap >= q[2])
}
}
return
}
# Accepted solution for LeetCode #3161: Block Placement Queries
from sortedcontainers import SortedList
class FenwickTree:
def __init__(self, n: int):
self.vals = [0] * (n + 1)
def maximize(self, i: int, val: int) -> None:
while i < len(self.vals):
self.vals[i] = max(self.vals[i], val)
i += FenwickTree.lowtree(i)
def get(self, i: int) -> int:
res = 0
while i > 0:
res = max(res, self.vals[i])
i -= FenwickTree.lowtree(i)
return res
@staticmethod
def lowtree(i: int) -> int:
return i & -i
class Solution:
def getResults(self, queries: list[list[int]]) -> list[bool]:
n = min(50000, len(queries) * 3)
ans = []
tree = FenwickTree(n + 1)
obstacles = SortedList([0, n]) # sentinel values
for query in queries:
type = query[0]
if type == 1:
x = query[1]
obstacles.add(x)
for x1, x2 in itertools.pairwise(obstacles):
tree.maximize(x2, x2 - x1)
for query in reversed(queries):
type = query[0]
x = query[1]
if type == 1:
i = obstacles.index(x)
next = obstacles[i + 1]
prev = obstacles[i - 1]
obstacles.remove(x)
tree.maximize(next, next - prev)
else:
sz = query[2]
i = obstacles.bisect_right(x)
prev = obstacles[i - 1]
ans.append(tree.get(prev) >= sz or x - prev >= sz)
return ans[::-1]
// Accepted solution for LeetCode #3161: Block Placement Queries
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3161: Block Placement Queries
// class FenwickTree {
// public FenwickTree(int n) {
// vals = new int[n + 1];
// }
//
// public void add(int i, int val) {
// while (i < vals.length) {
// vals[i] = Math.max(vals[i], val);
// i += lowbit(i);
// }
// }
//
// public int get(int i) {
// int res = 0;
// while (i > 0) {
// res = Math.max(res, vals[i]);
// i -= lowbit(i);
// }
// return res;
// }
//
// private int[] vals;
//
// private static int lowbit(int i) {
// return i & -i;
// }
// }
//
// class Solution {
// public List<Boolean> getResults(int[][] queries) {
// final int n = Math.min(50000, queries.length * 3);
// List<Boolean> ans = new ArrayList<>();
// FenwickTree tree = new FenwickTree(n + 1);
// TreeSet<Integer> obstacles = new TreeSet<>(Arrays.asList(0, n)); // sentinel values
//
// for (int[] query : queries) {
// final int type = query[0];
// if (type == 1) {
// final int x = query[1];
// obstacles.add(x);
// }
// }
//
// Iterator<Integer> it = obstacles.iterator();
// int x1 = it.next();
// while (it.hasNext()) {
// final int x2 = it.next();
// tree.add(x2, x2 - x1);
// x1 = x2;
// }
//
// for (int i = queries.length - 1; i >= 0; --i) {
// final int type = queries[i][0];
// final int x = queries[i][1];
// if (type == 1) {
// final Integer next = obstacles.higher(x);
// final Integer prev = obstacles.lower(x);
// if (next != null)
// tree.add(next, next - prev);
// obstacles.remove(x);
// } else {
// final int sz = queries[i][2];
// final int prev = obstacles.floor(x);
// ans.add(tree.get(prev) >= sz || x - prev >= sz);
// }
// }
//
// Collections.reverse(ans);
// return ans;
// }
// }
// Accepted solution for LeetCode #3161: Block Placement Queries
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3161: Block Placement Queries
// class FenwickTree {
// public FenwickTree(int n) {
// vals = new int[n + 1];
// }
//
// public void add(int i, int val) {
// while (i < vals.length) {
// vals[i] = Math.max(vals[i], val);
// i += lowbit(i);
// }
// }
//
// public int get(int i) {
// int res = 0;
// while (i > 0) {
// res = Math.max(res, vals[i]);
// i -= lowbit(i);
// }
// return res;
// }
//
// private int[] vals;
//
// private static int lowbit(int i) {
// return i & -i;
// }
// }
//
// class Solution {
// public List<Boolean> getResults(int[][] queries) {
// final int n = Math.min(50000, queries.length * 3);
// List<Boolean> ans = new ArrayList<>();
// FenwickTree tree = new FenwickTree(n + 1);
// TreeSet<Integer> obstacles = new TreeSet<>(Arrays.asList(0, n)); // sentinel values
//
// for (int[] query : queries) {
// final int type = query[0];
// if (type == 1) {
// final int x = query[1];
// obstacles.add(x);
// }
// }
//
// Iterator<Integer> it = obstacles.iterator();
// int x1 = it.next();
// while (it.hasNext()) {
// final int x2 = it.next();
// tree.add(x2, x2 - x1);
// x1 = x2;
// }
//
// for (int i = queries.length - 1; i >= 0; --i) {
// final int type = queries[i][0];
// final int x = queries[i][1];
// if (type == 1) {
// final Integer next = obstacles.higher(x);
// final Integer prev = obstacles.lower(x);
// if (next != null)
// tree.add(next, next - prev);
// obstacles.remove(x);
// } else {
// final int sz = queries[i][2];
// final int prev = obstacles.floor(x);
// ans.add(tree.get(prev) >= sz || x - prev >= sz);
// }
// }
//
// Collections.reverse(ans);
// return ans;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.