Detailed Explanation of Segment Tree Interval Maximum Subarray Sum Query and Update Operations
Problem Description
Given an array \(arr\) of length \(n\), we wish to efficiently support the following two operations:
- Query Operation: Query the maximum subarray sum (i.e., the sum of the contiguous subarray with the largest sum) of any interval \([l, r]\).
- Update Operation: Modify the value at a specific position \(i\) to \(val\).
The goal is to implement a data structure that minimizes the time complexity for both operations (typically aiming for \(O(\log n)\) level).
Background Knowledge
This is a classic advanced application problem of segment trees. Ordinary interval sum queries cannot directly yield the maximum subarray sum because the maximum subarray may span both left and right sub-intervals.
To complete queries and updates within \(O(\log n)\) time, we need to maintain four values in each node of the segment tree and compute the parent node's value through a merging strategy.
Solution Process
Step 1: Understand the Properties of "Maximum Subarray Sum"
For an interval, its maximum subarray sum (denoted as mx) can originate from three possibilities:
- Entirely within the left sub-interval.
- Entirely within the right sub-interval.
- Spanning across the left and right sub-intervals (i.e., the maximum suffix sum of the left sub-interval + the maximum prefix sum of the right sub-interval).
To calculate the spanning case, we also need to know for each interval:
- Interval Sum (
sum): The sum of all elements in the interval. - Maximum Prefix Sum (
pre): The maximum contiguous sum starting from the left endpoint of the interval. - Maximum Suffix Sum (
suf): The maximum contiguous sum ending at the right endpoint of the interval.
Step 2: Define the Segment Tree Node Structure
Each node (corresponding to an interval) stores four values:
class Node:
def __init__(self):
self.sum = 0 # Interval sum
self.pre = 0 # Maximum prefix sum
self.suf = 0 # Maximum suffix sum
self.mx = 0 # Maximum subarray sum
Step 3: Merge Two Sub-intervals to Obtain the Parent Interval
Assuming the left child node is left and the right child node is right, the parent node res is calculated as follows:
- Interval Sum:
res.sum = left.sum + right.sum - Maximum Prefix Sum:
res.pre = max(left.pre, left.sum + right.pre)
(Either use only the left part, or extend into the right part) - Maximum Suffix Sum:
res.suf = max(right.suf, right.sum + left.suf) - Maximum Subarray Sum:
res.mx = max(left.mx, right.mx, left.suf + right.pre)
(Take the maximum of the three cases)
Example:
Array [3, -1, 2, 4]
Left sub-interval [3, -1]: sum=2, pre=3, suf=2, mx=3 (subarray [3])
Right sub-interval [2, 4]: sum=6, pre=6, suf=6, mx=6 (subarray [2,4])
After merging:
sum = 2+6=8pre = max(3, 2+6)=max(3,8)=8suf = max(6, 6+2)=max(6,8)=8mx = max(3, 6, 2+6)=max(3,6,8)=8(corresponding to the sum of subarray[3,-1,2,4])
Step 4: Build Tree (Initialization)
- Recursively split the array into halves until reaching leaf nodes.
- Leaf node:
sum=pre=suf=mx=arr[i](If empty subarrays are allowed, negative numbers need consideration, but typically the maximum subarray sum must include at least one element, so directly take this value). - Merge child node information from bottom to top.
Time Complexity: \(O(n)\), each node is calculated once.
Step 5: Point Update
- Recursively traverse from the root to the target leaf node and update its value.
- Re-merge the four values for all nodes along the path during backtracking.
Time Complexity: \(O(\log n)\), only one path is updated.
Step 6: Interval Query
The query interval may span multiple nodes, requiring the merging of multiple nodes' information:
- If the current node's interval is completely within the query interval, directly return the node's four values.
- If it lies entirely on one side, recurse on that side.
- If it spans both left and right, query both sides separately, then merge the two results (using the same merging method as in Step 3).
Note: A temporary node is needed to store the merged result.
Example Illustration
Array: arr = [1, -3, 5, 2]
After building the tree, the root node represents interval [0,3]:
- Left sub-interval
[0,1](values[1,-3]):sum=-2, pre=1, suf=-2, mx=1 - Right sub-interval
[2,3](values[5,2]):sum=7, pre=7, suf=7, mx=7 - After merging:
sum = -2+7=5pre = max(1, -2+7)=max(1,5)=5suf = max(7, 7+(-2))=max(7,5)=7mx = max(1, 7, (-2)+7)=max(1,7,5)=7(corresponding to subarray[5,2])
The maximum subarray sum is 7, corresponding to the right sub-interval's[5,2]. Correct.
Query interval [0,2] (values [1,-3,5]):
- Left sub-interval
[0,1]as above:sum=-2, pre=1, suf=-2, mx=1 - Right sub-interval
[2,2](leaf5):sum=5, pre=5, suf=5, mx=5 - After merging:
sum=3pre = max(1, -2+5)=max(1,3)=3suf = max(5, 5+(-2))=max(5,3)=5mx = max(1, 5, (-2)+5)=max(1,5,3)=5
The result 5 corresponds to subarray[5](actually, the maximum contiguous sum in[1,-3,5]is 5).
Summary
By maintaining the four values sum, pre, suf, mx, the segment tree can handle interval maximum subarray sum queries and point updates within \(O(\log n)\) time. This is a classic example of using segment trees to solve complex interval merging problems.