Queries to find maximum product pair in range with updates

Given an array of N positive integers. The task is to perform the following operations according to the type of query given.
1. Print the maximum pair product in a given range. [L-R]
2. Update Ai with some given value.

Examples:

Input: A={1, 3, 4, 2, 5}
Queries:
Type 1: L = 0, R = 2.
Type 2: i = 1, val = 6
Type 1: L = 0, R = 2.

Output:
12
24

For the query1, the maximum product in a range [0, 2] is 3*4 = 12.
For the query2, after an update, the array becomes [1, 6, 4, 2, 5]
For the query3, the maximum product in a range [0, 2] is 6*4 = 24.



Naive Solution: The brute force approach is to traverse from L to R and check for every pair and then find the maximum product pair among them.
Time Complexity: O(N^2) for every query.

A better solution is to find the first and the second largest number in the range L to R by traversing and then returning their product.
Time Complexity: O(N) for every query.

A efficient solution is to use a segment tree to store the largest and second largest number in the nodes and then return the product of them.

Below is the implementation of above approach.

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ program to find the maximum
// product in a range with updates
#include <bits/stdc++.h>
using namespace std;
#define ll long long
  
// structure defined
struct segment {
    // l for largest
    // sl for second largest
    ll l;
    ll sl;
};
  
// function to perform queries
segment query(segment* tree, ll index,
              ll s, ll e, ll qs, ll qe)
{
  
    segment res;
    res.l = -1;
    res.sl = -1;
    // no overlapping case
    if (qs > e || qe < s || s > e) {
  
        return res;
    }
  
    // complete overlap case
    if (s >= qs && e <= qe) {
  
        return tree[index];
    }
  
    // partial overlap case
    ll mid = (s + e) / 2;
  
    // calling left node and right node
    segment left = query(tree, 2 * index,
                         s, mid, qs, qe);
    segment right = query(tree, 2 * index + 1,
                          mid + 1, e, qs, qe);
  
    // largest of ( left.l, right.l)
    ll largest = max(left.l, right.l);
  
    // compute second largest
    // second largest will be minimum
    // of maximum from left and right node
    ll second_largest = min(max(left.l, right.sl),
                            max(right.l, left.sl));
  
    // store largest and
    // second_largest in res
    res.l = largest;
    res.sl = second_largest;
  
    // return the resulting node
    return res;
}
  
// funcntion to update the query
void update(segment* tree, ll index,
            ll s, ll e, ll i, ll val)
{
    // no overlapping case
    if (i < s || i > e) {
        return;
    }
  
    // reached leaf node
  
    if (s == e) {
        tree[index].l = val;
        tree[index].sl = INT_MIN;
        return;
    }
  
    // partial overlap
  
    ll mid = (s + e) / 2;
  
    // left subtree call
    update(tree, 2 * index, s, mid, i, val);
  
    // right subtree call
    update(tree, 2 * index + 1, mid + 1, e, i, val);
  
    // largest of ( left.l, right.l)
    tree[index].l = max(tree[2 * index].l, tree[2 * index + 1].l);
  
    // compute second largest
    // second largest will be
    // minimum of maximum from left and right node
  
    tree[index].sl = min(max(tree[2 * index].l, tree[2 * index + 1].sl),
                         max(tree[2 * index + 1].l, tree[2 * index].sl));
}
  
// Function to build the tree
void buildtree(segment* tree, ll* a, ll index, ll s, ll e)
{
    // tree is build bottom to up
    if (s > e) {
        return;
    }
  
    // leaf node
    if (s == e) {
        tree[index].l = a[s];
        tree[index].sl = INT_MIN;
        return;
    }
  
    ll mid = (s + e) / 2;
  
    // calling the left node
    buildtree(tree, a, 2 * index, s, mid);
  
    // calling the right node
    buildtree(tree, a, 2 * index + 1, mid + 1, e);
  
    // largest of ( left.l, right.l)
    ll largest = max(tree[2 * index].l, tree[2 * index + 1].l);
  
    // compute second largest
    // second largest will be minimum
    // of maximum from left and right node
    ll second_largest = min(max(tree[2 * index].l, tree[2 * index + 1].sl),
                            max(tree[2 * index + 1].l, tree[2 * index].sl));
  
    // storing the largest and
    // second_largest values in the current node
    tree[index].l = largest;
    tree[index].sl = second_largest;
}
  
// Driver Code
int main()
{
  
    // your code goes here
    ll n = 5;
  
    ll a[5] = { 1, 3, 4, 2, 5 };
  
    // allocating memory for segment tree
    segment* tree = new segment[4 * n + 1];
  
    // buildtree(tree, a, index, start, end)
    buildtree(tree, a, 1, 0, n - 1);
  
    // query section
    // storing the resulting node
    segment res = query(tree, 1, 0, n - 1, 0, 2);
  
    cout << "Maximum product in the range "
         << "0 and 2 before update: " << (res.l * res.sl);
  
    // update section
    // update(tree, index, start, end, i, v)
    update(tree, 1, 0, n - 1, 1, 6);
  
    res = query(tree, 1, 0, n - 1, 0, 2);
  
    cout << "\nMaximum product in the range "
         << "0 and 2 after update: " << (res.l * res.sl);
  
    return 0;
}

chevron_right


Output:

Maximum product in the range 0 and 2 before update: 12
Maximum product in the range 0 and 2 after update: 24

Time Complexity: O(log N) per query and O(N) for building the tree.



My Personal Notes arrow_drop_up

Maths is the language of nature

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.