Sum and Maximum of elements in array from [L, R] before and after updates
Prerequisite: Segment Trees, Lazy Propagation in Segment Tree.
Given an array arr[] of N integers. The task is to do the following operations:
- Change the value arr[i] to min(arr[i], X) where X is an integer for a given range [L, R].
- Find the maximum value from index L to R where 0 ≤ L ≤ R ≤ N-1 before and after the update given to the array above.
- Find the sum of the element from index L to R where 0 ≤ L ≤ R ≤ N-1 before and after the update given to the array above.
Examples:
Input: arr[] = {1, 2, 3, 4, 5}, L = 2, R = 4, X = 3
Output:
Maximum in range [2, 4] before update: 5
Sum in range [2, 4] before update: 12
Maximum in range [2, 4] after update: 3
Sum in range [2, 4] after update: 9
Explanation:
Before Update:
arr[] = {1, 2, 3, 4, 5}
The maximum value from [L, R] is 5
Sum in range [L, R] is 3 + 4 + 5 = 12After Update:
arr[] = {1, 2, 3, 3, 3}
The maximum value from [L, R] is 3
Sum in range [L, R] is 3 + 3 + 3 = 9Input: arr[] = {1, 4, 19, 0, 7, 22, 7}, L = 1, R = 5, X = 14
Output:
Maximum in range [1, 5] before update: 22
Sum in range [1, 5] before update: 52
Maximum in range [1, 5] after update: 22
Sum in range [1, 5] after update: 39
Explanation:
Before Update:
arr[] = {1, 4, 19, 0, 7, 22, 7}
The maximum value from [L, R] is 22
Sum in range [L, R] is 4 + 19 + 0 + 7 + 22 = 52After Update:
arr[] = {1, 4, 14, 0, 7, 14, 7}
The maximum value from [L, R] is 14
Sum in range [L, R] is 4 + 14 + 0 + 7 + 14 = 39
Approach:
Few Terms for Lazy Propagation:
- pushdown(): The lazy tag is applied to current node and then is pushed down to its children.
- tag_condition: It is the condition that needs to be satisfied to set the lazy node. In normal lazy trees, it is generally the condition that ranges of the node covers lie entirely in the update range.
- A node in the segment tree represents a range of the array. Now all elements in that range will have different values and they will change by different amounts during an update. So we need the information about distinct values and their counts in that range. So this becomes a worst-case O(N) operation as at max N distinct nodes in a range.
- Below is the approach to solve these restrictions in Segment Tree. Each node in the Segment Tree will have the following values:
- value: The value of a range, here sum of all elements in that range
- maxval: Maximum value in that range
- secondmax: Strict second maximum value in that range
- cnt_max: Count of maximum value in that range
- cnt_secondmax: Count of secondmax in that range
Below are the steps:
- Form a Segment Tree for the given array element with every nodes having the properties mentioned above.
- For updateQuery() call do the following:
- If the max_value is less than the updated value(say X), then no value will be changed as min(arr[i], X) will be arr[i] itself for this case.
- If secondmax ≤ X ≤ maxvalue, then update the value of that node and the newSum will be calculated by:
new_sum = sum - f*maxvalue + f*X, where f is the frequency of maxvalue
- Query for find the maximum number in the range [L, R]:
- If range [L, R] completely lies outside the range, then return 0.
- If range [L, R] completely lies inside the range, then maximum value for the current range is tree[pos].mx1.
- Else check the above condition for left and right subtree recursively.
- The maximum value for all the above recursive call gives the maximum value in the range [L, R].
- Query for find the sum in the range [L, R]:
- If range [L, R] completely lies outside the range, then return 0.
- If range [L, R] completely lies inside the range, then the sum for the current range is tree[pos].sum.
- Else check the above condition for left and right subtree recursively.
- The sum of all value for above recursive call gives the sum in the range [L, R].
Below is the implementation of the above approach:
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Node for each Segment Tree typedef struct node { int sum; int mx1; int mx2; int cnt_mx1; int cnt_mx2; // Constructor node() { sum = mx1 = mx2 = 0; cnt_mx1 = cnt_mx2 = 0; } } node; const int N = 1e5 + 5; // Segment Tree Node node tree[N]; // For Lazy Propagation int lazy[N]; // Function to merge 2 nodes of Segment // Tree void combine( int pos) { // Map to store the count of // maximum1 and maximum2 for every // node segment tree map< int , int > x; // Update the count for left and // right subtree x[tree[2 * pos + 1].mx1] += tree[2 * pos + 1].cnt_mx1; x[tree[2 * pos + 1].mx2] += tree[2 * pos + 1].cnt_mx2; x[tree[2 * pos + 2].mx1] += tree[2 * pos + 2].cnt_mx1; x[tree[2 * pos + 2].mx2] += tree[2 * pos + 2].cnt_mx2; // Vector pair to store mx1 & mx2 vector<pair< int , int > > v; // Traverse the v for ( auto it = x.begin(); it != x.end(); it++) { v.push_back({ it->first, it->second }); } int n = v.size(); // Update the mx1 and mx2 after // combined node tree[pos].mx1 = v[n - 1].first; tree[pos].cnt_mx1 = v[n - 1].second; // If only one node if (n == 1) { tree[pos].mx2 = tree[pos].cnt_mx2 = 0; } // ELse Update mx2 and cnt_mx2 else { tree[pos].mx2 = v[n - 2].first; tree[pos].cnt_mx2 = v[n - 2].second; } // Update the sum tree[pos].sum = tree[2 * pos + 1].sum + tree[2 * pos + 2].sum; } // Function that returns true if tag // condition is satisfied, and we can // do lazy update bool tag_condition( int pos, int x) { if (tree[pos].mx1 > x && tree[pos].mx2 <= x) { return true ; } return false ; } // Function that pushes down the lazy // value of the current node to its children void pushdown( int beg, int end, int pos) { // If tag condition satisfies if (tag_condition(pos, lazy[pos])) { int initsum = tree[pos].mx1 * tree[pos].cnt_mx1; int finsum = lazy[pos] * tree[pos].cnt_mx1; tree[pos].sum += finsum - initsum; // If only one node, then update the // maximum value to current position if (beg == end) tree[pos].mx1 = lazy[pos]; // If lazy[pos] > maximum value else { // Update mx1 to current // lazy[pos] if (lazy[pos] > tree[pos].mx2) tree[pos].mx1 = lazy[pos]; // Else update the count else { tree[pos].mx1 = lazy[pos]; tree[pos].cnt_mx1 += tree[pos].cnt_mx2; tree[pos].mx2 = tree[pos].cnt_mx2 = 0; // map to store the cnt // of maximum1 and maximum2 // for every node in segment // tree map< int , int > x; x[tree[2 * pos + 1].mx1] += tree[2 * pos + 1].cnt_mx1; x[tree[2 * pos + 1].mx2] += tree[2 * pos + 1].cnt_mx2; x[tree[2 * pos + 2].mx1] += tree[2 * pos + 2].cnt_mx1; x[tree[2 * pos + 2].mx2] += tree[2 * pos + 2].cnt_mx2; // Traverse the map for ( auto it = x.begin(); it != x.end(); it++) { // Update the maximum // count if (it->first != tree[pos].mx1 && it->first > tree[pos].mx2) { tree[pos].mx2 = it->first; tree[pos].cnt_mx2 = it->second; } } } // Update the value for // lazy left and right // subtree lazy[2 * pos + 1] = min(lazy[2 * pos + 1], lazy[pos]); lazy[2 * pos + 2] = min(lazy[2 * pos + 2], lazy[pos]); } } lazy[pos] = INT_MAX; } // Function that Lazy update in segment // tree i.e., arr[i] = min(arr[i], val) void update( int beg, int end, int l, int r, int pos, int val) { // Push the current node value to // left and right subtree if (lazy[pos] < INT_MAX) pushdown(beg, end, pos); // If inside the range, then update // the value as per the conditions if (l <= beg and r >= end && tag_condition(pos, val)) { lazy[pos] = min(lazy[pos], val); pushdown(beg, end, pos); return ; } // Outside the range else if (l > end || r < beg || beg > end || tree[pos].mx1 <= val) { return ; } // Check for left and right subtree else { int mid = (beg + end) / 2; update(beg, mid, l, r, 2 * pos + 1, val); update(mid + 1, end, l, r, 2 * pos + 2, val); combine(pos); } } // Function that returns the maximum // value in range [L, R] int query1( int beg, int end, int l, int r, int pos) { // Push the current node value in // the left and right subtree if (lazy[pos] < INT_MAX) { pushdown(beg, end, pos); } // If inside the range, then return // the maximum value if (l <= beg && r >= end) { return tree[pos].mx1; } // Outside the range else if (l > end || r < beg || beg > end) { return 0; } // Check for left and right subtree else { int mid = (beg + end) / 2; return max(query1(beg, mid, l, r, 2 * pos + 1), query1(mid + 1, end, l, r, 2 * pos + 2)); } } // Function to find the sum in the // range [L, R] int query2( int beg, int end, int l, int r, int pos) { // Push the current node value if (lazy[pos] < INT_MAX) pushdown(beg, end, pos); // If in the range, return the // sum if (l <= beg and r >= end) return tree[pos].sum; else if (l > end || r < beg || beg > end) { return 0; } // Check for left and right subtree else { int mid = (beg + end) / 2; return query2(beg, mid, l, r, 2 * pos + 1) + query1(mid + 1, end, l, r, 2 * pos + 2); } } // Construct Segment Tree void constr( int arr[], int beg, int end, int pos) { // If only a single node if (beg == end) { int x = arr[beg]; tree[pos].sum = x; tree[pos].mx1 = x; tree[pos].cnt_mx1 = 1; tree[pos].mx2 = 0; tree[pos].cnt_mx2 = 0; return ; } // Recursively update for // left and right subtree else { int mid = (beg + end) / 2; // For Left subtree constr(arr, beg, mid, 2 * pos + 1); // For right subtree constr(arr, mid + 1, end, 2 * pos + 2); // Combine the two left and // right subtree combine(pos); } } // A utility function to construct // the segment tree void construct( int arr[], int n) { for ( int i = 0; i < N; i++) { lazy[i] = INT_MAX; } // Function call to Construct // segment tree constr(arr, 0, n - 1, 0); } // Driver Code int main() { int arr[] = { 1, 2, 3, 4, 5 }; // Construct segment tree construct(arr, 5); cout << "Maximum in [2, 4] before update: " ; // Query for maximum in range [0, 4] cout << query1(0, 4, 2, 4, 0) << endl; cout << "Sum in [2, 4] before update: " ; // Query for sum in range [0, 4] cout << query2(0, 4, 2, 4, 0) << endl; // Update Query update(0, 4, 2, 5, 0, 3); cout << endl; cout << "Updated array elements between " << "[2, 4] as min(arr[i], 3)" << endl; cout << endl; cout << "Maximum in [2, 4] after update: " ; // Query for maximum in range [0, 4] cout << query1(0, 4, 2, 4, 0) << endl; cout << "Sum in [2, 4] after update: " ; // Query for maximum in range [0, 4] cout << query2(0, 4, 2, 4, 0) << endl; return 0; } |
Maximum in [2, 4] before update: 5 Sum in [2, 4] before update: 8 Updated array elements between [2, 4] as min(arr[i], 3) Maximum in [2, 4] after update: 3 Sum in [2, 4] after update: 6
Time Complexity: O(N*log N)
Please Login to comment...