Open In App

Sum and Maximum of elements in array from [L, R] before and after updates

Improve
Improve
Like Article
Like
Save
Share
Report

Prerequisite: Segment Trees, Lazy Propagation in Segment Tree. Given an array arr[] of N integers. The task is to do the following operations:

  1. Change the value arr[i] to min(arr[i], X) where X is an integer for a given range [L, R].
  2. 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.
  3. 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 = 12 
After Update: arr[] = {1, 2, 3, 3, 3} The maximum value from [L, R] is 3 Sum in range [L, R] is 3 + 3 + 3 = 9 

Input: 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 = 52
 After 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:

  1. pushdown(): The lazy tag is applied to current node and then is pushed down to its children.
  2. 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.
  3. 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.
  4. 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:

  1. Form a Segment Tree for the given array element with every nodes having the properties mentioned above.
  2. 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
  1. 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].
  2. 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: 

CPP




// 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;
}


Output:

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)
Auxiliary Space: O(N)



Last Updated : 28 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads