Skip to content

Commit ed229a5

Browse files
committed
UI enhancements
1 parent 3020ed2 commit ed229a5

9 files changed

Lines changed: 2726 additions & 602 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# https://leetcode.com/problems/maximum-non-negative-product-in-a-matrix/description/
2+
3+
'''
4+
You are given a m x n matrix grid. Initially, you are located at the top-left corner (0, 0), and in each step, you can only move right or down in the matrix.
5+
Among all possible paths starting from the top-left corner (0, 0) and ending in the bottom-right corner (m - 1, n - 1), find the path with the maximum non-negative product. The product of a path is the product of all integers in the grid cells visited along the path.
6+
Return the maximum non-negative product modulo 109 + 7. If the maximum product is negative, return -1.
7+
Notice that the modulo is performed after getting the maximum product.
8+
9+
10+
Example 1:
11+
Input: grid = [[-1,-2,-3],[-2,-3,-3],[-3,-3,-2]]
12+
Output: -1
13+
Explanation: It is not possible to get non-negative product in the path from (0, 0) to (2, 2), so return -1.
14+
15+
Example 2:
16+
Input: grid = [[1,-2,1],[1,-2,1],[3,-4,1]]
17+
Output: 8
18+
Explanation: Maximum non-negative product is shown (1 * 1 * -2 * -4 * 1 = 8).
19+
20+
Example 3:
21+
Input: grid = [[1,3],[0,-4]]
22+
Output: 0
23+
Explanation: Maximum non-negative product is shown (1 * 0 * -4 = 0).
24+
25+
Constraints:
26+
m == grid.length
27+
n == grid[i].length
28+
1 <= m, n <= 15
29+
-4 <= grid[i][j] <= 4
30+
'''
31+
32+
class Solution:
33+
MOD=10**9+7
34+
def maxProductPath(self, grid: List[List[int]]) -> int:
35+
m,n=len(grid),len(grid[0])
36+
maxgt=[[0]*n for _ in range(m)]
37+
minlt=[[0]*n for _ in range(m)]
38+
39+
maxgt[0][0]=minlt[0][0]=grid[0][0]
40+
41+
for i in range(1,n):
42+
maxgt[0][i]=minlt[0][i]=maxgt[0][i-1]*grid[0][i]
43+
for i in range(1,m):
44+
maxgt[i][0]=minlt[i][0]=maxgt[i-1][0]*grid[i][0]
45+
46+
for i in range(1,m):
47+
for j in range(1,n):
48+
if grid[i][j]>=0:
49+
maxgt[i][j]=max(maxgt[i][j-1],maxgt[i-1][j])*grid[i][j]
50+
minlt[i][j]=min(minlt[i][j-1],minlt[i-1][j])*grid[i][j]
51+
else:
52+
maxgt[i][j]=min(minlt[i][j-1],minlt[i-1][j])*grid[i][j]
53+
minlt[i][j]=max(maxgt[i][j-1],maxgt[i-1][j])*grid[i][j]
54+
55+
return maxgt[m-1][n-1]%self.MOD if maxgt[m-1][n-1]>=0 else -1
56+
57+
58+
'''
59+
Complexity Analysis
60+
Let m and n be the number of rows and columns of the matrix.
61+
62+
Time complexity: O(mn).
63+
We traverse each cell once, and each transition takes constant time.
64+
65+
Space complexity: O(mn).
66+
We maintain two matrices of size m×n.
67+
'''

Segment Trees/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
This directory stores advanced range query data structures in Python.
44

5+
## Sqrt decomposition (`SQRT_DECOMPOSITION.md`)
6+
- Splits an array into blocks of size about `sqrt(n)`.
7+
- Precomputes one summary per block (sum/min/max/frequency, depending on the problem).
8+
- Best fit when:
9+
- You need something simpler than a segment tree.
10+
- Constraints are moderate (`n, q` around `10^5`).
11+
- Queries are online and updates are not too heavy.
12+
- Typical complexity:
13+
- Build: `O(n)`
14+
- Query: `O(sqrt(n))`
15+
- Point update: `O(1)` or `O(sqrt(n))`, depending on block metadata
16+
- Closely related: Mo's algorithm uses the same block intuition for offline queries.
17+
518
## Fenwick tree (`feenwick.py`)
619
- Supports point updates and prefix sums in `O(log n)` using least-significant-bit jumps.
720
- Ideal for:
@@ -26,3 +39,12 @@ This directory stores advanced range query data structures in Python.
2639
## When to use which
2740
- Fenwick tree: easier to code, handles prefix queries and range updates with small tweaks.
2841
- Segment tree: more flexible (min/max, gcd, lazy propagation, two-dimensional variants).
42+
- Sqrt decomposition: simpler than a segment tree, strong fallback for online range queries when `O(sqrt(n))` is fast enough.
43+
- Mo's algorithm: use for offline range queries, especially distinct-count and frequency-style problems.
44+
45+
## Contest decision guide
46+
- Static range sum, no updates: prefix sum.
47+
- Prefix/range sum with point updates: Fenwick tree.
48+
- Complex online range queries or heavy updates: segment tree.
49+
- Medium constraints and faster implementation pressure: sqrt decomposition.
50+
- Offline frequency/distinct queries: Mo's algorithm.
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# Square Root Decomposition
2+
3+
Square root decomposition is a block-based range-query technique. Instead of processing every element inside a query range, split the array into blocks of size about `sqrt(n)` and precompute one answer per block.
4+
5+
It belongs to the broader topic of data structures and range query optimization.
6+
7+
## What it does
8+
9+
- Break an array into blocks of size about `sqrt(n)`.
10+
- Store precomputed information for each block.
11+
- Answer queries by combining:
12+
- a partial prefix block
13+
- zero or more full blocks
14+
- a partial suffix block
15+
16+
This is usually much faster than brute force and much easier to implement than a segment tree.
17+
18+
## Where it fits
19+
20+
- Data Structures
21+
- Range Queries
22+
- Query Processing
23+
- Block Decomposition
24+
25+
Closely related topics:
26+
27+
- Prefix Sum
28+
- Fenwick Tree
29+
- Segment Tree
30+
- Mo's Algorithm
31+
32+
## When to use it
33+
34+
Use sqrt decomposition when:
35+
36+
- You have many range queries on an array.
37+
- Updates exist, but the problem does not justify a full segment tree.
38+
- `O(sqrt(n))` per query is fast enough for the given constraints.
39+
- You want a contest-safe implementation with low bug risk.
40+
41+
Common use cases:
42+
43+
- Range sum
44+
- Range min / max
45+
- Frequency counting per block
46+
- Jump queries
47+
- Range updates with block rebuild ideas
48+
49+
## Complexity
50+
51+
- Build: `O(n)`
52+
- Query: `O(sqrt(n))`
53+
- Point update: `O(1)` for block sums, sometimes `O(sqrt(n))` if block metadata must be rebuilt
54+
- Space: `O(n)`
55+
56+
## Core intuition
57+
58+
Array:
59+
60+
```text
61+
[1, 2, 3, 4, 5, 6, 7, 8, 9]
62+
```
63+
64+
Choose block size `3`:
65+
66+
```text
67+
[1,2,3] [4,5,6] [7,8,9]
68+
```
69+
70+
Store block sums:
71+
72+
```text
73+
[6, 15, 24]
74+
```
75+
76+
Now query sum from index `2` to `7`:
77+
78+
- Take partial left block: `3`
79+
- Take full middle block: `4 + 5 + 6 = 15`
80+
- Take partial right block: `7 + 8`
81+
82+
Total: `26`
83+
84+
The key idea is that full blocks are answered in `O(1)` each.
85+
86+
## Python template
87+
88+
```python
89+
import math
90+
91+
92+
class SqrtDecomposition:
93+
def __init__(self, nums):
94+
self.n = len(nums)
95+
self.block_size = int(math.sqrt(self.n)) + 1
96+
self.nums = nums[:]
97+
self.blocks = [0] * ((self.n + self.block_size - 1) // self.block_size)
98+
99+
for i, val in enumerate(self.nums):
100+
self.blocks[i // self.block_size] += val
101+
102+
def update(self, idx, val):
103+
block_idx = idx // self.block_size
104+
self.blocks[block_idx] += val - self.nums[idx]
105+
self.nums[idx] = val
106+
107+
def query(self, left, right):
108+
total = 0
109+
start_block = left // self.block_size
110+
end_block = right // self.block_size
111+
112+
if start_block == end_block:
113+
for i in range(left, right + 1):
114+
total += self.nums[i]
115+
return total
116+
117+
end_of_start = min(self.n, (start_block + 1) * self.block_size)
118+
for i in range(left, end_of_start):
119+
total += self.nums[i]
120+
121+
for block in range(start_block + 1, end_block):
122+
total += self.blocks[block]
123+
124+
start_of_end = end_block * self.block_size
125+
for i in range(start_of_end, right + 1):
126+
total += self.nums[i]
127+
128+
return total
129+
130+
131+
nums = [1, 2, 3, 4, 5, 6, 7, 8]
132+
sq = SqrtDecomposition(nums)
133+
134+
print(sq.query(2, 6)) # 25
135+
sq.update(3, 10) # nums[3] = 10
136+
print(sq.query(2, 6)) # 31
137+
```
138+
139+
## Decision map
140+
141+
### 1. Prefix Sum
142+
143+
Use when:
144+
145+
- There are only queries.
146+
- There are no updates.
147+
- The operation is additive and easy to prefix.
148+
149+
Complexity:
150+
151+
- Build: `O(n)`
152+
- Query: `O(1)`
153+
154+
### 2. Fenwick Tree
155+
156+
Use when:
157+
158+
- You need point updates.
159+
- Queries are prefix sums or reducible to prefix sums.
160+
- You want `O(log n)` with a compact implementation.
161+
162+
Complexity:
163+
164+
- Query: `O(log n)`
165+
- Update: `O(log n)`
166+
167+
### 3. Segment Tree
168+
169+
Use when:
170+
171+
- Queries are more complex: min, max, gcd, custom merge.
172+
- You need many updates and many queries.
173+
- You may need lazy propagation.
174+
175+
Complexity:
176+
177+
- Query: `O(log n)`
178+
- Update: `O(log n)`
179+
180+
### 4. Sqrt Decomposition
181+
182+
Use when:
183+
184+
- You want simpler code.
185+
- Constraints are moderate.
186+
- `O(sqrt(n))` is acceptable.
187+
188+
Complexity:
189+
190+
- Query: `O(sqrt(n))`
191+
- Update: `O(1)` or `O(sqrt(n))`
192+
193+
### 5. Mo's Algorithm
194+
195+
Use when:
196+
197+
- Queries are offline.
198+
- The answer depends on frequencies or distinct counts.
199+
- Reordering queries helps maintain a moving window.
200+
201+
Complexity:
202+
203+
- About `O((n + q) * sqrt(n))`
204+
205+
## Quick contest checklist
206+
207+
Ask:
208+
209+
1. Are there updates?
210+
2. Are the queries online or offline?
211+
3. Is the operation simple sum/frequency, or a custom merge?
212+
4. Is implementation speed more important than asymptotic optimality?
213+
214+
Typical choices:
215+
216+
- No updates: prefix sum
217+
- Sum with point updates: Fenwick tree
218+
- Complex range query: segment tree
219+
- Simple and good enough: sqrt decomposition
220+
- Offline distinct/frequency query: Mo's algorithm
221+
222+
## Interview and contest notes
223+
224+
- Sqrt decomposition is often the easiest correct solution under time pressure.
225+
- It is a strong stepping stone before learning segment trees and Mo's algorithm.
226+
- If `O(sqrt(n))` is too slow, upgrade to Fenwick tree or segment tree.
227+
- If the problem is offline and frequency-based, think about Mo's algorithm immediately.
228+
229+
## Practice ideas
230+
231+
- Range sum query with point update
232+
- Range minimum query with updates
233+
- Distinct elements in range
234+
- Frequency of a value in range
235+
- Jump query / teleport blocks
236+
- Offline range query reordering with Mo's algorithm
237+
238+
## Recommended study order
239+
240+
Prefix Sum -> Fenwick Tree -> Sqrt Decomposition -> Segment Tree -> Mo's Algorithm

0 commit comments

Comments
 (0)