Queries to find maximum product pair in range with updates
You are given an array arr[]
of positive integers. Your task is to process q
queries, each consisting of three integers, of the following two types:
[1, L, R]
: Find the maximum product of any two distinct elements in the subarrayarr[L…R]
(inclusive).[2, i, val]
: Update the element at indexi
toval
(i.e., setarr[i] = val
) where val > 0
Examples:
Input: n = 5, q = 3
arr[] = [1, 3, 4, 2, 5]
queries[][] = [ [1, 0, 2], [2, 1, 6], [1, 0, 2] ]
Output: 12 24
Explanation: For the query 1, the maximum product in a range [0, 2] is 3 * 4 = 12.
For the query 2, after an update, the array becomes [1, 6, 4, 2, 5]
For the query 3, the maximum product in a range [0, 2] is 6 * 4 = 24.
Table of Content
[Naive Approach] - O(n * n * q) Time and O(1) Space
The idea is to examine every possible pair of elements in the range
[L, R]
, compute the product for each pair, and keep track of the maximum product found. By brute‑forcing all combinations, we guarantee that the highest possible product pair is identified.
Follow the below given steps:
- Initialize a variable
maxProduct
to a very small value and prepare variables to hold the indices or values of the best pair. - Iterate an outer loop variable
i
fromL
toR
. - Within that, iterate an inner loop variable
j
fromi + 1
toR
. - Compute
currentProduct = arr[i] * arr[j]
. - If
currentProduct
exceedsmaxProduct
, updatemaxProduct
and record the corresponding pair(arr[i], arr[j])
. - Continue until all pairs have been evaluated.
- Return or print the pair that produced
maxProduct
.
Below is given the implementation:
#include <bits/stdc++.h>
using namespace std;
vector<int> solveQueries(vector<int> &arr,
vector<vector<int>> &queries) {
int n = arr.size();
// to store the result of query of type 1
vector<int> res;
// process all the queries
for(auto query: queries) {
// to find the maximum
// product in the range [L, R]
if(query[0] == 1) {
int l = query[1];
int r = query[2];
int maxProd = 0;
// find all possible paris
for(int i = l; i < r; i++) {
for(int j = i + 1; j <= r; j++) {
maxProd = max(maxProd, arr[i] * arr[j]);
}
}
res.push_back(maxProd);
}
// else update the value of arr[i]
else if(query[0] == 2) {
int i = query[1];
int x = query[2];
arr[i] = x;
}
}
return res;
}
int main() {
vector<int> arr = {1, 3, 4, 2, 5};
vector<vector<int>> queries =
{ {1, 0, 2}, {2, 1, 6}, {1, 0, 2}};
vector<int> res = solveQueries(arr, queries);
for(auto i:res) {
cout << i << " ";
}
return 0;
}
import java.util.*;
class GfG {
public static ArrayList<Integer> solveQueries(
ArrayList<Integer> arr, ArrayList<ArrayList<Integer>> queries) {
int n = arr.size();
// to store the result of query of type 1
ArrayList<Integer> res = new ArrayList<>();
// process all the queries
for (ArrayList<Integer> query : queries) {
// to find the maximum
// product in the range [L, R]
if (query.get(0) == 1) {
int l = query.get(1);
int r = query.get(2);
int maxProd = 0;
// find all possible paris
for (int i = l; i < r; i++) {
for (int j = i + 1; j <= r; j++) {
maxProd = Math.max(maxProd, arr.get(i) * arr.get(j));
}
}
res.add(maxProd);
}
// else update the value of arr[i]
else if (query.get(0) == 2) {
int i = query.get(1);
int x = query.get(2);
arr.set(i, x);
}
}
return res;
}
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<>(Arrays.asList(1, 3, 4, 2, 5));
ArrayList<ArrayList<Integer>> queries = new ArrayList<>();
queries.add(new ArrayList<>(Arrays.asList(1, 0, 2)));
queries.add(new ArrayList<>(Arrays.asList(2, 1, 6)));
queries.add(new ArrayList<>(Arrays.asList(1, 0, 2)));
ArrayList<Integer> res = solveQueries(arr, queries);
for (int i : res) {
System.out.print(i + " ");
}
}
}
def solveQueries(arr, queries):
n = len(arr)
# to store the result of query of type 1
res = []
# process all the queries
for query in queries:
# to find the maximum
# product in the range [L, R]
if query[0] == 1:
l = query[1]
r = query[2]
maxProd = 0
# find all possible paris
for i in range(l, r):
for j in range(i + 1, r + 1):
maxProd = max(maxProd, arr[i] * arr[j])
res.append(maxProd)
# else update the value of arr[i]
elif query[0] == 2:
i = query[1]
x = query[2]
arr[i] = x
return res
arr = [1, 3, 4, 2, 5]
queries = [[1, 0, 2], [2, 1, 6], [1, 0, 2]]
res = solveQueries(arr, queries)
for i in res:
print(i, end=" ")
using System;
using System.Collections.Generic;
class GfG {
public static List<int> solveQueries(List<int> arr,
List<List<int>> queries) {
int n = arr.Count;
// to store the result of query of type 1
List<int> res = new List<int>();
// process all the queries
foreach (var query in queries) {
// to find the maximum
// product in the range [L, R]
if (query[0] == 1) {
int l = query[1];
int r = query[2];
int maxProd = 0;
// find all possible paris
for (int i = l; i < r; i++) {
for (int j = i + 1; j <= r; j++) {
maxProd = Math.Max(maxProd, arr[i] * arr[j]);
}
}
res.Add(maxProd);
}
// else update the value of arr[i]
else if (query[0] == 2) {
int i = query[1];
int x = query[2];
arr[i] = x;
}
}
return res;
}
public static void Main() {
List<int> arr = new List<int> {1, 3, 4, 2, 5};
List<List<int>> queries = new List<List<int>> {
new List<int> {1, 0, 2},
new List<int> {2, 1, 6},
new List<int> {1, 0, 2}
};
List<int> res = solveQueries(arr, queries);
foreach (int i in res) {
Console.Write(i + " ");
}
}
}
function solveQueries(arr, queries) {
let n = arr.length;
// to store the result of query of type 1
let res = [];
// process all the queries
for (let query of queries) {
// to find the maximum
// product in the range [L, R]
if (query[0] === 1) {
let l = query[1];
let r = query[2];
let maxProd = 0;
// find all possible paris
for (let i = l; i < r; i++) {
for (let j = i + 1; j <= r; j++) {
maxProd = Math.max(maxProd, arr[i] * arr[j]);
}
}
res.push(maxProd);
}
// else update the value of arr[i]
else if (query[0] === 2) {
let i = query[1];
let x = query[2];
arr[i] = x;
}
}
return res;
}
let arr = [1, 3, 4, 2, 5];
let queries = [[1, 0, 2], [2, 1, 6], [1, 0, 2]];
let res = solveQueries(arr, queries);
for (let i of res) {
process.stdout.write(i + " ");
}
Output
12 24
[Expected Approach] - Using Segment Tree - O(q * log n) Time and O(n) Space
The idea is to use a segment tree where each node stores the largest and second largest elements within its corresponding segment. When a range query is performed, the maximum product is calculated by multiplying the two largest elements within that range. This approach ensures efficient query and update operations in logarithmic time.
Follow the below given steps:
- Define a structure to store the largest and second largest elements at each node of the segment tree.
- Build the segment tree from the input array, storing values in a bottom-up manner.
- For a query to find the maximum product in a range
[L, R]
, traverse the segment tree and merge results from overlapping segments, selecting the two largest elements. - Compute the product of the two values retrieved and store the result.
- For an update operation, traverse to the specific index and update the value in the segment tree, then adjust the parent nodes accordingly.
- Collect results from all queries of type 1 and return them.
Below is given the implementation:
#include <bits/stdc++.h>
using namespace std;
// structure defined
class segment {
public:
// l for largest
// sl for second largest
int l;
int sl;
};
// function to perform queries
segment processQuery(segment* tree,
int index, int s, int e, int qs, int 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
int mid = (s + e) / 2;
// calling left node and right node
segment left = processQuery(tree, 2 * index, s, mid, qs, qe);
segment right
= processQuery(tree, 2 * index + 1, mid + 1, e, qs, qe);
// largest of ( left.l, right.l)
int largest = max(left.l, right.l);
// compute second largest
// second largest will be minimum
// of maximum from left and right node
int 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;
}
// function to update the query
void update(segment* tree, int index,
int s, int e, int i, int 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
int 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, vector<int> &arr,
int index, int s, int e) {
// tree is build bottom to up
if (s > e) {
return;
}
// leaf node
if (s == e) {
tree[index].l = arr[s];
tree[index].sl = INT_MIN;
return;
}
int mid = (s + e) / 2;
// calling the left node
buildtree(tree, arr, 2 * index, s, mid);
// calling the right node
buildtree(tree, arr, 2 * index + 1, mid + 1, e);
// largest of ( left.l, right.l)
int 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
int 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;
}
vector<int> solveQueries(vector<int> &arr,
vector<vector<int>> &queries) {
int n = arr.size();
// to store the result of query of type 1
vector<int> res;
// allocating memory for segment tree
segment* tree = new segment[4 * n + 1];
// buildtree(tree, arr, index, start, end)
buildtree(tree, arr, 1, 0, n - 1);
for(auto query: queries) {
// to find the maximum
// product in the range [L, R]
if(query[0] == 1) {
int l = query[1];
int r = query[2];
// storing the resulting node
segment ans = processQuery(tree, 1, 0, n - 1, l, r);
ans.l *= ans.sl;
res.push_back(ans.l);
}
// else update the value of arr[i]
else if(query[0] == 2) {
int i = query[1];
int x = query[2];
update(tree, 1, 0, n - 1, i, x);
}
}
return res;
}
int main() {
vector<int> arr = {1, 3, 4, 2, 5};
vector<vector<int>> queries =
{ {1, 0, 2}, {2, 1, 6}, {1, 0, 2}};
vector<int> res = solveQueries(arr, queries);
for(auto i:res) {
cout << i << " ";
}
return 0;
}
import java.util.*;
class GfG {
public static class segment {
public int l;
public int sl;
}
public static segment processQuery(segment[] tree,
int index, int s, int e, int qs, int qe) {
segment res = new segment();
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
int mid = (s + e) / 2;
segment left = processQuery(tree, 2 * index, s, mid, qs, qe);
segment right
= processQuery(tree, 2 * index + 1, mid + 1, e, qs, qe);
int largest = Math.max(left.l, right.l);
int second_largest
= Math.min(Math.max(left.l, left.sl), Math.max(right.l, left.sl));
res.l = largest;
res.sl = second_largest;
return res;
}
public static void update(segment[] tree, int index,
int s, int e, int i, int val) {
// no overlapping case
if (i < s || i > e) {
return;
}
// reached leaf node
if (s == e) {
tree[index].l = val;
tree[index].sl = Integer.MIN_VALUE;
return;
}
int mid = (s + e) / 2;
update(tree, 2 * index, s, mid, i, val);
update(tree, 2 * index + 1, mid + 1, e, i, val);
tree[index].l
= Math.max(tree[2 * index].l, tree[2 * index + 1].l);
tree[index].sl = Math.min(
Math.max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.max(tree[2 * index + 1].l, tree[2 * index].sl));
}
public static void buildtree(segment[] tree, ArrayList<Integer> arr,
int index, int s, int e) {
if (s > e) {
return;
}
// leaf node
if (s == e) {
tree[index].l = arr.get(s);
tree[index].sl = Integer.MIN_VALUE;
return;
}
int mid = (s + e) / 2;
buildtree(tree, arr, 2 * index, s, mid);
buildtree(tree, arr, 2 * index + 1, mid + 1, e);
int largest
= Math.max(tree[2 * index].l, tree[2 * index + 1].l);
int second_largest = Math.min(
Math.max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.max(tree[2 * index + 1].l, tree[2 * index].sl));
tree[index].l = largest;
tree[index].sl = second_largest;
}
public static ArrayList<Integer> solveQueries(ArrayList<Integer> arr, ArrayList<ArrayList<Integer>> queries) {
int n = arr.size();
// to store the result of query of type 1
ArrayList<Integer> res = new ArrayList<>();
segment[] tree = new segment[4 * n + 1];
for (int i = 0; i < tree.length; i++) {
tree[i] = new segment();
}
buildtree(tree, arr, 1, 0, n - 1);
for (ArrayList<Integer> query : queries) {
// to find the maximum
// product in the range [L, R]
if (query.get(0) == 1) {
int l = query.get(1);
int r = query.get(2);
segment ans = processQuery(tree, 1, 0, n - 1, l, r);
ans.l *= ans.sl;
res.add(ans.l);
}
// else update the value of arr[i]
else if (query.get(0) == 2) {
int i = query.get(1);
int x = query.get(2);
update(tree, 1, 0, n - 1, i, x);
}
}
return res;
}
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<>(Arrays.asList(1, 3, 4, 2, 5));
ArrayList<ArrayList<Integer>> queries = new ArrayList<>();
queries.add(new ArrayList<>(Arrays.asList(1, 0, 2)));
queries.add(new ArrayList<>(Arrays.asList(2, 1, 6)));
queries.add(new ArrayList<>(Arrays.asList(1, 0, 2)));
ArrayList<Integer> res = solveQueries(arr, queries);
for (int i : res) {
System.out.print(i + " ");
}
}
}
class segment:
def __init__(self):
self.l = 0
self.sl = 0
def processQuery(tree,
index, s, e, qs, qe):
res = segment()
res.l = -1
res.sl = -1
# no overlapping case
if qs > e or qe < s or s > e:
return res
# complete overlap case
if s >= qs and e <= qe:
return tree[index]
# partial overlap case
mid = (s + e) // 2
left = processQuery(tree, 2 * index, s, mid, qs, qe)
right = processQuery(tree, 2 * index + 1, mid + 1, e, qs, qe)
largest = max(left.l, right.l)
second_largest = min(max(left.l, left.sl), max(right.l, left.sl))
res.l = largest
res.sl = second_largest
return res
def update(tree, index,
s, e, i, val):
# no overlapping case
if i < s or i > e:
return
# reached leaf node
if s == e:
tree[index].l = val
tree[index].sl = float('-inf')
return
mid = (s + e) // 2
update(tree, 2 * index, s, mid, i, val)
update(tree, 2 * index + 1, mid + 1, e, i, val)
tree[index].l = max(tree[2 * index].l, tree[2 * index + 1].l)
tree[index].sl = min(
max(tree[2 * index].l, tree[2 * index + 1].sl),
max(tree[2 * index + 1].l, tree[2 * index].sl))
def buildtree(tree, arr,
index, s, e):
if s > e:
return
# leaf node
if s == e:
tree[index].l = arr[s]
tree[index].sl = float('-inf')
return
mid = (s + e) // 2
buildtree(tree, arr, 2 * index, s, mid)
buildtree(tree, arr, 2 * index + 1, mid + 1, e)
largest = max(tree[2 * index].l, tree[2 * index + 1].l)
second_largest = min(
max(tree[2 * index].l, tree[2 * index + 1].sl),
max(tree[2 * index + 1].l, tree[2 * index].sl))
tree[index].l = largest
tree[index].sl = second_largest
def solveQueries(arr, queries):
n = len(arr)
# to store the result of query of type 1
res = []
tree = [segment() for _ in range(4 * n + 1)]
buildtree(tree, arr, 1, 0, n - 1)
for query in queries:
# to find the maximum
# product in the range [L, R]
if query[0] == 1:
l = query[1]
r = query[2]
ans = processQuery(tree, 1, 0, n - 1, l, r)
ans.l *= ans.sl
res.append(ans.l)
# else update the value of arr[i]
elif query[0] == 2:
i = query[1]
x = query[2]
update(tree, 1, 0, n - 1, i, x)
return res
if __name__ == "__main__":
arr = [1, 3, 4, 2, 5]
queries = [[1, 0, 2], [2, 1, 6], [1, 0, 2]]
res = solveQueries(arr, queries)
for i in res:
print(i, end=" ")
using System;
using System.Collections.Generic;
class GfG {
public class segment {
public int l;
public int sl;
}
public static segment processQuery(segment[] tree,
int index, int s, int e, int qs, int qe) {
segment res = new segment();
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
int mid = (s + e) / 2;
segment left = processQuery(tree, 2 * index, s, mid, qs, qe);
segment right
= processQuery(tree, 2 * index + 1, mid + 1, e, qs, qe);
int largest = Math.Max(left.l, right.l);
int second_largest
= Math.Min(Math.Max(left.l, left.sl),
Math.Max(right.l, left.sl));
res.l = largest;
res.sl = second_largest;
return res;
}
public static void update(segment[] tree, int index,
int s, int e, int i, int val) {
// no overlapping case
if (i < s || i > e) {
return;
}
// reached leaf node
if (s == e) {
tree[index].l = val;
tree[index].sl = int.MinValue;
return;
}
int mid = (s + e) / 2;
update(tree, 2 * index, s, mid, i, val);
update(tree, 2 * index + 1, mid + 1, e, i, val);
tree[index].l
= Math.Max(tree[2 * index].l, tree[2 * index + 1].l);
tree[index].sl = Math.Min(
Math.Max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.Max(tree[2 * index + 1].l, tree[2 * index].sl));
}
public static void buildtree(segment[] tree, List<int> arr,
int index, int s, int e) {
if (s > e) {
return;
}
// leaf node
if (s == e) {
tree[index].l = arr[s];
tree[index].sl = int.MinValue;
return;
}
int mid = (s + e) / 2;
buildtree(tree, arr, 2 * index, s, mid);
buildtree(tree, arr, 2 * index + 1, mid + 1, e);
int largest
= Math.Max(tree[2 * index].l, tree[2 * index + 1].l);
int second_largest = Math.Min(
Math.Max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.Max(tree[2 * index + 1].l, tree[2 * index].sl));
tree[index].l = largest;
tree[index].sl = second_largest;
}
public static List<int> solveQueries(
List<int> arr, List<List<int>> queries) {
int n = arr.Count;
// to store the result of query of type 1
List<int> res = new List<int>();
segment[] tree = new segment[4 * n + 1];
for (int i = 0; i < tree.Length; i++) {
tree[i] = new segment();
}
buildtree(tree, arr, 1, 0, n - 1);
foreach (var query in queries) {
// to find the maximum
// product in the range [L, R]
if (query[0] == 1) {
int l = query[1];
int r = query[2];
segment ans = processQuery(tree, 1, 0, n - 1, l, r);
ans.l *= ans.sl;
res.Add(ans.l);
}
// else update the value of arr[i]
else if (query[0] == 2) {
int i = query[1];
int x = query[2];
update(tree, 1, 0, n - 1, i, x);
}
}
return res;
}
public static void Main() {
List<int> arr = new List<int> {1, 3, 4, 2, 5};
List<List<int>> queries = new List<List<int>> {
new List<int> {1, 0, 2},
new List<int> {2, 1, 6},
new List<int> {1, 0, 2}
};
List<int> res = solveQueries(arr, queries);
foreach (int i in res) {
Console.Write(i + " ");
}
}
}
class segment {
constructor() {
this.l = 0;
this.sl = 0;
}
}
function processQuery(tree,
index, s, e, qs, qe) {
let res = new segment();
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
let mid = Math.floor((s + e) / 2);
let left = processQuery(tree, 2 * index, s, mid, qs, qe);
let right = processQuery(tree, 2 * index + 1, mid + 1, e, qs, qe);
let largest = Math.max(left.l, right.l);
let second_largest = Math.min(
Math.max(left.l, left.sl), Math.max(right.l, left.sl)
);
res.l = largest;
res.sl = second_largest;
return res;
}
function update(tree, index,
s, e, i, val) {
// no overlapping case
if (i < s || i > e) {
return;
}
// reached leaf node
if (s === e) {
tree[index].l = val;
tree[index].sl = Number.MIN_SAFE_INTEGER;
return;
}
let mid = Math.floor((s + e) / 2);
update(tree, 2 * index, s, mid, i, val);
update(tree, 2 * index + 1, mid + 1, e, i, val);
tree[index].l = Math.max(tree[2 * index].l, tree[2 * index + 1].l);
tree[index].sl = Math.min(
Math.max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.max(tree[2 * index + 1].l, tree[2 * index].sl)
);
}
function buildtree(tree, arr,
index, s, e) {
if (s > e) {
return;
}
// leaf node
if (s === e) {
tree[index].l = arr[s];
tree[index].sl = Number.MIN_SAFE_INTEGER;
return;
}
let mid = Math.floor((s + e) / 2);
buildtree(tree, arr, 2 * index, s, mid);
buildtree(tree, arr, 2 * index + 1, mid + 1, e);
let largest = Math.max(tree[2 * index].l, tree[2 * index + 1].l);
let second_largest = Math.min(
Math.max(tree[2 * index].l, tree[2 * index + 1].sl),
Math.max(tree[2 * index + 1].l, tree[2 * index].sl)
);
tree[index].l = largest;
tree[index].sl = second_largest;
}
function solveQueries(arr, queries) {
let n = arr.length;
// to store the result of query of type 1
let res = [];
let tree = new Array(4 * n + 1).fill(null).map(() => new segment());
buildtree(tree, arr, 1, 0, n - 1);
for (let query of queries) {
// to find the maximum
// product in the range [L, R]
if (query[0] === 1) {
let l = query[1];
let r = query[2];
let ans = processQuery(tree, 1, 0, n - 1, l, r);
ans.l *= ans.sl;
res.push(ans.l);
}
// else update the value of arr[i]
else if (query[0] === 2) {
let i = query[1];
let x = query[2];
update(tree, 1, 0, n - 1, i, x);
}
}
return res;
}
let arr = [1, 3, 4, 2, 5];
let queries = [[1, 0, 2], [2, 1, 6], [1, 0, 2]];
let res = solveQueries(arr, queries);
for (let i of res) {
process.stdout.write(i + " ");
}
Output
12 24