class SubrectangleQueries:
def __init__(self, rectangle):
self.rectangle = [row[:] for row in rectangle]
self.updates = []
def updateSubrectangle(self, row1, col1, row2, col2, newValue):
self.updates.append((row1, col1, row2, col2, newValue))
def getValue(self, row, col):
for r1, c1, r2, c2, val in reversed(self.updates):
if r1 <= row <= r2 and c1 <= col <= c2:
return val
return self.rectangle[row][col]
class SubrectangleQueries {
public:
vector<vector<int>> rect;
vector<tuple<int,int,int,int,int>> updates;
SubrectangleQueries(vector<vector<int>>& rectangle) {
rect = rectangle;
}
void updateSubrectangle(int row1, int col1, int row2, int col2, int newValue) {
updates.push_back({row1, col1, row2, col2, newValue});
}
int getValue(int row, int col) {
for (int i = updates.size()-1; i >= 0; --i) {
auto [r1, c1, r2, c2, val] = updates[i];
if (r1 <= row && row <= r2 && c1 <= col && col <= c2)
return val;
}
return rect[row][col];
}
};
class SubrectangleQueries {
private int[][] rectangle;
private List<int[]> updates;
public SubrectangleQueries(int[][] rectangle) {
this.rectangle = new int[rectangle.length][];
for (int i = 0; i < rectangle.length; ++i)
this.rectangle[i] = rectangle[i].clone();
updates = new ArrayList<>();
}
public void updateSubrectangle(int row1, int col1, int row2, int col2, int newValue) {
updates.add(new int[]{row1, col1, row2, col2, newValue});
}
public int getValue(int row, int col) {
for (int i = updates.size()-1; i >= 0; --i) {
int[] u = updates.get(i);
if (u[0] <= row && row <= u[2] && u[1] <= col && col <= u[3])
return u[4];
}
return rectangle[row][col];
}
}
var SubrectangleQueries = function(rectangle) {
this.rectangle = rectangle.map(row => row.slice());
this.updates = [];
};
SubrectangleQueries.prototype.updateSubrectangle = function(row1, col1, row2, col2, newValue) {
this.updates.push([row1, col1, row2, col2, newValue]);
};
SubrectangleQueries.prototype.getValue = function(row, col) {
for (let i = this.updates.length - 1; i >= 0; --i) {
let [r1, c1, r2, c2, val] = this.updates[i];
if (r1 <= row && row <= r2 && c1 <= col && col <= c2)
return val;
}
return this.rectangle[row][col];
};
The Subrectangle Queries problem involves designing a class to efficiently perform updates and queries on a 2D grid (rectangle) of integers.
You are given a 2D array called rectangle
. The task is to implement a class with the following methods:
updateSubrectangle(row1, col1, row2, col2, newValue)
: Updates all values in the subrectangle defined by its upper left corner (row1, col1)
and bottom right corner (row2, col2)
to newValue
.getValue(row, col)
: Returns the current value at cell (row, col)
in the rectangle.updateSubrectangle
and getValue
calls.
At first glance, it seems straightforward: for each updateSubrectangle
call, simply iterate through all the relevant cells and set their value. However, if the rectangle is large and there are many updates, this approach becomes inefficient.
The challenge is to support both frequent updates and queries efficiently. The brute-force approach (updating every cell in the subrectangle each time) is slow. Instead, we can consider a more efficient way: record each update as an operation rather than applying it immediately.
When a getValue
is called, we check if any updates affect the requested cell, starting from the most recent update. If so, return the value from the update; otherwise, return the original value from the rectangle.
This approach is efficient when the number of updates is much less than the number of cells in the rectangle, and it avoids unnecessary repeated writes.
getValue(row, col)
, iterate through the updates list in reverse order (from most recent to oldest). For each update, check if the cell (row, col)
is inside the updated subrectangle. If found, return the update's value immediately. If none match, return the original value from the rectangle.Why does this work? Because the most recent update that covers a cell determines its value. We only need to find the latest update that affects the cell, so we search updates in reverse.
Suppose our initial rectangle is:
[[1,2,1],
[4,3,4],
[3,2,1],
[1,1,1]]
Step 1: updateSubrectangle(0, 0, 3, 2, 5)
All cells become 5. But we just record this update: (0,0,3,2,5)
.
Step 2: getValue(0, 2)
We check the updates in reverse (only one so far). The cell (0,2) is within (0,0)-(3,2), so we return 5.
Step 3: updateSubrectangle(3, 0, 3, 2, 10)
Now, the last row is updated to 10. We add this update: (3,0,3,2,10)
.
Step 4: getValue(3, 1)
We check updates in reverse. The latest update covers (3,1), so we return 10.
Step 5: getValue(0, 2)
The latest update only covers row 3, but (0,2) is not in row 3. The previous update covers (0,2), so we return 5.
The optimized approach is faster for updates and is efficient if the number of updates is much less than the number of cells. If there are many updates and queries, further optimizations may be possible.
The Subrectangle Queries problem demonstrates how to trade off between update and query efficiency by recording update operations instead of applying them immediately. This approach is particularly effective when updates are sparse relative to the size of the rectangle, allowing for quick updates and reasonable query times. The key insight is to always check the most recent relevant update for any cell, ensuring correctness and efficiency.