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.
You are given an array of positive integers nums and a positive integer k. You are also given a 2D array queries, where queries[i] = [indexi, valuei, starti, xi].
You are allowed to perform an operation once on nums, where you can remove any suffix from nums such that nums remains non-empty.
The x-value of nums for a given x is defined as the number of ways to perform this operation so that the product of the remaining elements leaves a remainder of x modulo k.
For each query in queries you need to determine the x-value of nums for xi after performing the following actions:
nums[indexi] to valuei. Only this step persists for the rest of the queries.nums[0..(starti - 1)] (where nums[0..(-1)] will be used to represent the empty prefix).Return an array result of size queries.length where result[i] is the answer for the ith query.
A prefix of an array is a subarray that starts from the beginning of the array and extends to any point within it.
A suffix of an array is a subarray that starts at any point within the array and extends to the end of the array.
Note that the prefix and suffix to be chosen for the operation can be empty.
Note that x-value has a different definition in this version.
Example 1:
Input: nums = [1,2,3,4,5], k = 3, queries = [[2,2,0,2],[3,3,3,0],[0,1,0,1]]
Output: [2,2,2]
Explanation:
nums becomes [1, 2, 2, 4, 5], and the empty prefix must be removed. The possible operations are:
[2, 4, 5]. nums becomes [1, 2].nums becomes [1, 2, 2, 4, 5] with a product 80, which gives remainder 2 when divided by 3.nums becomes [1, 2, 2, 3, 5], and the prefix [1, 2, 2] must be removed. The possible operations are:
nums becomes [3, 5].[5]. nums becomes [3].nums becomes [1, 2, 2, 3, 5], and the empty prefix must be removed. The possible operations are:
[2, 2, 3, 5]. nums becomes [1].[3, 5]. nums becomes [1, 2, 2].Example 2:
Input: nums = [1,2,4,8,16,32], k = 4, queries = [[0,2,0,2],[0,2,0,1]]
Output: [1,0]
Explanation:
nums becomes [2, 2, 4, 8, 16, 32]. The only possible operation is:
[2, 4, 8, 16, 32].nums becomes [2, 2, 4, 8, 16, 32]. There is no possible way to perform the operation.Example 3:
Input: nums = [1,1,2,1,1], k = 2, queries = [[2,1,0,1]]
Output: [5]
Constraints:
1 <= nums[i] <= 1091 <= nums.length <= 1051 <= k <= 51 <= queries.length <= 2 * 104queries[i] == [indexi, valuei, starti, xi]0 <= indexi <= nums.length - 11 <= valuei <= 1090 <= starti <= nums.length - 10 <= xi <= k - 1Problem summary: You are given an array of positive integers nums and a positive integer k. You are also given a 2D array queries, where queries[i] = [indexi, valuei, starti, xi]. You are allowed to perform an operation once on nums, where you can remove any suffix from nums such that nums remains non-empty. The x-value of nums for a given x is defined as the number of ways to perform this operation so that the product of the remaining elements leaves a remainder of x modulo k. For each query in queries you need to determine the x-value of nums for xi after performing the following actions: Update nums[indexi] to valuei. Only this step persists for the rest of the queries. Remove the prefix nums[0..(starti - 1)] (where nums[0..(-1)] will be used to represent the empty prefix). Return an array result of size queries.length where result[i] is the answer for the ith query. A prefix of an array is a
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Math · Segment Tree
[1,2,3,4,5] 3 [[2,2,0,2],[3,3,3,0],[0,1,0,1]]
[1,2,4,8,16,32] 4 [[0,2,0,2],[0,2,0,1]]
[1,1,2,1,1] 2 [[2,1,0,1]]
longest-uploaded-prefix)minimum-sum-of-values-by-dividing-array)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3525: Find X Value of Array II
// Auto-generated Java example from go.
class Solution {
public void exampleSolution() {
}
}
// Reference (go):
// // Accepted solution for LeetCode #3525: Find X Value of Array II
// package main
//
// import (
// "math/bits"
// )
//
// // https://space.bilibili.com/206214
// var k int
//
// type data struct {
// mul int
// cnt [5]int
// }
//
// type seg []data
//
// func mergeData(a, b data) data {
// cnt := a.cnt
// for rx, c := range b.cnt {
// cnt[a.mul*rx%k] += c
// }
// return data{a.mul * b.mul % k, cnt}
// }
//
// func newData(val int) data {
// mul := val % k
// cnt := [5]int{}
// cnt[mul] = 1
// return data{mul, cnt}
// }
//
// func (t seg) maintain(o int) {
// t[o] = mergeData(t[o<<1], t[o<<1|1])
// }
//
// func (t seg) build(a []int, o, l, r int) {
// if l == r {
// t[o] = newData(a[l])
// return
// }
// m := (l + r) >> 1
// t.build(a, o<<1, l, m)
// t.build(a, o<<1|1, m+1, r)
// t.maintain(o)
// }
//
// func (t seg) update(o, l, r, i, val int) {
// if l == r {
// t[o] = newData(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.maintain(o)
// }
//
// func (t seg) query(o, l, r, ql, qr int) data {
// if ql <= l && r <= qr {
// return t[o]
// }
// m := (l + r) / 2
// if qr <= m {
// return t.query(o*2, l, m, ql, qr)
// }
// if ql > m {
// return t.query(o*2+1, m+1, r, ql, qr)
// }
// lRes := t.query(o*2, l, m, ql, qr)
// rRes := t.query(o*2+1, m+1, r, ql, qr)
// return mergeData(lRes, rRes)
// }
//
// func newSegmentTreeWithArray(a []int) seg {
// n := len(a)
// t := make(seg, 2<<bits.Len(uint(n-1)))
// t.build(a, 1, 0, n-1)
// return t
// }
//
// func resultArray(nums []int, K int, queries [][]int) []int {
// k = K
// t := newSegmentTreeWithArray(nums)
// n := len(nums)
// ans := make([]int, len(queries))
// for qi, q := range queries {
// t.update(1, 0, n-1, q[0], q[1])
// res := t.query(1, 0, n-1, q[2], n-1)
// ans[qi] = res.cnt[q[3]]
// }
// return ans
// }
// Accepted solution for LeetCode #3525: Find X Value of Array II
package main
import (
"math/bits"
)
// https://space.bilibili.com/206214
var k int
type data struct {
mul int
cnt [5]int
}
type seg []data
func mergeData(a, b data) data {
cnt := a.cnt
for rx, c := range b.cnt {
cnt[a.mul*rx%k] += c
}
return data{a.mul * b.mul % k, cnt}
}
func newData(val int) data {
mul := val % k
cnt := [5]int{}
cnt[mul] = 1
return data{mul, cnt}
}
func (t seg) maintain(o int) {
t[o] = mergeData(t[o<<1], t[o<<1|1])
}
func (t seg) build(a []int, o, l, r int) {
if l == r {
t[o] = newData(a[l])
return
}
m := (l + r) >> 1
t.build(a, o<<1, l, m)
t.build(a, o<<1|1, m+1, r)
t.maintain(o)
}
func (t seg) update(o, l, r, i, val int) {
if l == r {
t[o] = newData(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.maintain(o)
}
func (t seg) query(o, l, r, ql, qr int) data {
if ql <= l && r <= qr {
return t[o]
}
m := (l + r) / 2
if qr <= m {
return t.query(o*2, l, m, ql, qr)
}
if ql > m {
return t.query(o*2+1, m+1, r, ql, qr)
}
lRes := t.query(o*2, l, m, ql, qr)
rRes := t.query(o*2+1, m+1, r, ql, qr)
return mergeData(lRes, rRes)
}
func newSegmentTreeWithArray(a []int) seg {
n := len(a)
t := make(seg, 2<<bits.Len(uint(n-1)))
t.build(a, 1, 0, n-1)
return t
}
func resultArray(nums []int, K int, queries [][]int) []int {
k = K
t := newSegmentTreeWithArray(nums)
n := len(nums)
ans := make([]int, len(queries))
for qi, q := range queries {
t.update(1, 0, n-1, q[0], q[1])
res := t.query(1, 0, n-1, q[2], n-1)
ans[qi] = res.cnt[q[3]]
}
return ans
}
# Accepted solution for LeetCode #3525: Find X Value of Array II
class Solution:
def resultArray(self, nums: List[int], k: int, queries: List[List[int]]) -> List[int]:
n = len(nums)
m = 1
while m < n:
m *= 2
identity_prod = 1 % k
identity_counts = [0] * k
# tree[i] stores (product_modulo_k, counts_array)
# counts_array[rem] stores the number of prefixes in the range with product % k == rem
tree = [(identity_prod, list(identity_counts)) for _ in range(2 * m)]
# Initialize leaves
for i in range(n):
val = nums[i]
p = val % k
c = [0] * k
c[p] = 1
tree[m + i] = (p, c)
# Build tree
for i in range(m - 1, 0, -1):
lp, lc = tree[2 * i]
rp, rc = tree[2 * i + 1]
np = (lp * rp) % k
nc = lc[:]
for r, count in enumerate(rc):
if count > 0:
nc[(lp * r) % k] += count
tree[i] = (np, nc)
results = []
for index, value, start, x in queries:
# Update point
idx = m + index
p = value % k
c = [0] * k
c[p] = 1
tree[idx] = (p, c)
# Propagate changes up
curr = idx // 2
while curr > 0:
lp, lc = tree[2 * curr]
rp, rc = tree[2 * curr + 1]
np = (lp * rp) % k
nc = lc[:]
for r, count in enumerate(rc):
if count > 0:
nc[(lp * r) % k] += count
tree[curr] = (np, nc)
curr //= 2
# Query range [start, n-1]
l, r = m + start, m + n - 1
la_p, la_c = identity_prod, list(identity_counts)
ra_nodes = []
while l <= r:
if l % 2 == 1:
node_p, node_c = tree[l]
new_c = la_c[:]
for rem, count in enumerate(node_c):
if count > 0:
new_c[(la_p * rem) % k] += count
la_p = (la_p * node_p) % k
la_c = new_c
l += 1
if r % 2 == 0:
ra_nodes.append(tree[r])
r -= 1
l //= 2
r //= 2
# Merge right accumulator nodes in correct order
curr_p, curr_c = la_p, la_c
while ra_nodes:
node_p, node_c = ra_nodes.pop()
new_c = curr_c[:]
for rem, count in enumerate(node_c):
if count > 0:
new_c[(curr_p * rem) % k] += count
curr_p = (curr_p * node_p) % k
curr_c = new_c
results.append(curr_c[x])
return results
// Accepted solution for LeetCode #3525: Find X Value of Array II
// Rust example auto-generated from go 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 (go):
// // Accepted solution for LeetCode #3525: Find X Value of Array II
// package main
//
// import (
// "math/bits"
// )
//
// // https://space.bilibili.com/206214
// var k int
//
// type data struct {
// mul int
// cnt [5]int
// }
//
// type seg []data
//
// func mergeData(a, b data) data {
// cnt := a.cnt
// for rx, c := range b.cnt {
// cnt[a.mul*rx%k] += c
// }
// return data{a.mul * b.mul % k, cnt}
// }
//
// func newData(val int) data {
// mul := val % k
// cnt := [5]int{}
// cnt[mul] = 1
// return data{mul, cnt}
// }
//
// func (t seg) maintain(o int) {
// t[o] = mergeData(t[o<<1], t[o<<1|1])
// }
//
// func (t seg) build(a []int, o, l, r int) {
// if l == r {
// t[o] = newData(a[l])
// return
// }
// m := (l + r) >> 1
// t.build(a, o<<1, l, m)
// t.build(a, o<<1|1, m+1, r)
// t.maintain(o)
// }
//
// func (t seg) update(o, l, r, i, val int) {
// if l == r {
// t[o] = newData(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.maintain(o)
// }
//
// func (t seg) query(o, l, r, ql, qr int) data {
// if ql <= l && r <= qr {
// return t[o]
// }
// m := (l + r) / 2
// if qr <= m {
// return t.query(o*2, l, m, ql, qr)
// }
// if ql > m {
// return t.query(o*2+1, m+1, r, ql, qr)
// }
// lRes := t.query(o*2, l, m, ql, qr)
// rRes := t.query(o*2+1, m+1, r, ql, qr)
// return mergeData(lRes, rRes)
// }
//
// func newSegmentTreeWithArray(a []int) seg {
// n := len(a)
// t := make(seg, 2<<bits.Len(uint(n-1)))
// t.build(a, 1, 0, n-1)
// return t
// }
//
// func resultArray(nums []int, K int, queries [][]int) []int {
// k = K
// t := newSegmentTreeWithArray(nums)
// n := len(nums)
// ans := make([]int, len(queries))
// for qi, q := range queries {
// t.update(1, 0, n-1, q[0], q[1])
// res := t.query(1, 0, n-1, q[2], n-1)
// ans[qi] = res.cnt[q[3]]
// }
// return ans
// }
// Accepted solution for LeetCode #3525: Find X Value of Array II
// Auto-generated TypeScript example from go.
function exampleSolution(): void {
}
// Reference (go):
// // Accepted solution for LeetCode #3525: Find X Value of Array II
// package main
//
// import (
// "math/bits"
// )
//
// // https://space.bilibili.com/206214
// var k int
//
// type data struct {
// mul int
// cnt [5]int
// }
//
// type seg []data
//
// func mergeData(a, b data) data {
// cnt := a.cnt
// for rx, c := range b.cnt {
// cnt[a.mul*rx%k] += c
// }
// return data{a.mul * b.mul % k, cnt}
// }
//
// func newData(val int) data {
// mul := val % k
// cnt := [5]int{}
// cnt[mul] = 1
// return data{mul, cnt}
// }
//
// func (t seg) maintain(o int) {
// t[o] = mergeData(t[o<<1], t[o<<1|1])
// }
//
// func (t seg) build(a []int, o, l, r int) {
// if l == r {
// t[o] = newData(a[l])
// return
// }
// m := (l + r) >> 1
// t.build(a, o<<1, l, m)
// t.build(a, o<<1|1, m+1, r)
// t.maintain(o)
// }
//
// func (t seg) update(o, l, r, i, val int) {
// if l == r {
// t[o] = newData(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.maintain(o)
// }
//
// func (t seg) query(o, l, r, ql, qr int) data {
// if ql <= l && r <= qr {
// return t[o]
// }
// m := (l + r) / 2
// if qr <= m {
// return t.query(o*2, l, m, ql, qr)
// }
// if ql > m {
// return t.query(o*2+1, m+1, r, ql, qr)
// }
// lRes := t.query(o*2, l, m, ql, qr)
// rRes := t.query(o*2+1, m+1, r, ql, qr)
// return mergeData(lRes, rRes)
// }
//
// func newSegmentTreeWithArray(a []int) seg {
// n := len(a)
// t := make(seg, 2<<bits.Len(uint(n-1)))
// t.build(a, 1, 0, n-1)
// return t
// }
//
// func resultArray(nums []int, K int, queries [][]int) []int {
// k = K
// t := newSegmentTreeWithArray(nums)
// n := len(nums)
// ans := make([]int, len(queries))
// for qi, q := range queries {
// t.update(1, 0, n-1, q[0], q[1])
// res := t.query(1, 0, n-1, q[2], n-1)
// ans[qi] = res.cnt[q[3]]
// }
// return ans
// }
Use this to step through a reusable interview workflow for this problem.
For each of q queries, scan the entire range to compute the aggregate (sum, min, max). Each range scan takes up to O(n) for a full-array query. With q queries: O(n × q) total. Point updates are O(1) but queries dominate.
Building the tree is O(n). Each query or update traverses O(log n) nodes (tree height). For q queries: O(n + q log n) total. Space is O(4n) ≈ O(n) for the tree array. Lazy propagation adds O(1) per node for deferred updates.
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: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.