Facebook Pixel

2921. Maximum Profitable Triplets With Increasing Prices II ๐Ÿ”’

HardBinary Indexed TreeSegment TreeArray
Leetcode Link

Problem Description

You are given two arrays prices and profits, both of length n, where each index i represents an item in a store with prices[i] as its price and profits[i] as its profit.

Your task is to select exactly three items that satisfy a specific ordering condition:

  • The three items must be at indices i, j, and k where i < j < k
  • The prices must be strictly increasing: prices[i] < prices[j] < prices[k]

When you select three valid items, the total profit is calculated as profits[i] + profits[j] + profits[k].

The goal is to find the maximum possible profit from selecting three items that meet these conditions. If no valid selection of three items exists, return -1.

For example, if you have items with prices [1, 5, 3, 6] and profits [4, 3, 2, 5], you could select items at indices 0, 1, and 3 (with prices 1 < 5 < 6), giving you a total profit of 4 + 3 + 5 = 12.

The solution uses Binary Indexed Trees to efficiently track the maximum profit available to the left and right of each position. For each potential middle item j, it finds the best item i with a lower price on the left and the best item k with a higher price on the right, then combines their profits to find the optimal selection.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is to think of this problem as finding an optimal "middle" element. For any valid triplet (i, j, k), we can consider j as the middle element and ask: what's the best choice for i on the left and k on the right?

If we fix the middle element at position j, we need:

  • The best item i where i < j and prices[i] < prices[j] (to maximize profits[i])
  • The best item k where k > j and prices[k] > prices[j] (to maximize profits[k])

This transforms the problem from finding all possible triplets (which would be O(nยณ)) to iterating through each potential middle element and finding the best left and right partners (which can be done more efficiently).

The challenge is: how do we quickly find "the maximum profit among all items with price less than X" as we process the array? This is a classic range maximum query problem that can be solved with a Binary Indexed Tree (Fenwick Tree).

For the left side, we process items from left to right, and at each position, we query for the maximum profit among all previously seen items with smaller prices. We then update our data structure with the current item.

For the right side, we need items with larger prices. A clever trick is to process from right to left and use inverted prices (max_price + 1 - prices[i]). This inversion transforms "finding items with larger prices" into "finding items with smaller inverted prices", allowing us to use the same BIT query logic.

By precomputing the best left partner for each position in left[i] and the best right partner in right[i], we can then simply iterate through all positions as potential middle elements and calculate left[i] + profits[i] + right[i] to find the maximum total profit.

Learn more about Segment Tree patterns.

Solution Approach

The solution uses two Binary Indexed Trees (BITs) to efficiently maintain and query maximum profits based on price constraints.

Binary Indexed Tree Implementation: The BinaryIndexedTree class provides two key operations:

  • update(x, v): Updates position x with value v, maintaining the maximum value at each position
  • query(x): Returns the maximum value among all positions from 1 to x

Main Algorithm Steps:

  1. Initialize data structures:

    • Create arrays left[i] and right[i] to store the maximum profit to the left and right of each position
    • Find the maximum price m to determine the BIT size
    • Create two BITs: tree1 for left-side processing and tree2 for right-side processing
  2. Build left array (left-to-right pass):

    for i, x in enumerate(prices):
        left[i] = tree1.query(x - 1)  # Get max profit where price < x
        tree1.update(x, profits[i])   # Add current item to BIT
    • For each position i, query the BIT for the maximum profit among all items with prices less than prices[i]
    • Update the BIT with the current item's profit at price position
  3. Build right array (right-to-left pass):

    for i in range(n - 1, -1, -1):
        x = m + 1 - prices[i]          # Invert the price
        right[i] = tree2.query(x - 1)  # Get max profit where original price > prices[i]
        tree2.update(x, profits[i])    # Add current item to BIT
    • Process items from right to left
    • Use price inversion trick: m + 1 - prices[i] converts "finding larger prices" to "finding smaller inverted prices"
    • This allows us to use the same BIT query logic for both left and right sides
  4. Calculate maximum profit:

    return max((l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1)
    • For each position as the middle element, calculate total profit: left[i] + profits[i] + right[i]
    • Only consider positions where both left[i] and right[i] are non-zero (valid triplets exist)
    • Return the maximum profit found, or -1 if no valid triplet exists

Time Complexity: O(n log m) where n is the array length and m is the maximum price value Space Complexity: O(n + m) for the arrays and BIT structures

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a small example:

  • prices = [2, 4, 1, 5, 3]
  • profits = [3, 6, 7, 2, 8]

Step 1: Initialize

  • Maximum price m = 5
  • Create left and right arrays of size 5, initialized to 0
  • Create two Binary Indexed Trees with size 6 (to handle prices 1-5)

Step 2: Build left array (left-to-right pass)

Processing index 0 (price=2, profit=3):

  • Query BIT1 for max profit where price < 2: returns 0 (no items yet)
  • left[0] = 0
  • Update BIT1: position 2 gets value 3

Processing index 1 (price=4, profit=6):

  • Query BIT1 for max profit where price < 4: returns 3 (from price=2)
  • left[1] = 3
  • Update BIT1: position 4 gets value 6

Processing index 2 (price=1, profit=7):

  • Query BIT1 for max profit where price < 1: returns 0 (no smaller prices)
  • left[2] = 0
  • Update BIT1: position 1 gets value 7

Processing index 3 (price=5, profit=2):

  • Query BIT1 for max profit where price < 5: returns 7 (best among prices 1,2,4)
  • left[3] = 7
  • Update BIT1: position 5 gets value 2

Processing index 4 (price=3, profit=8):

  • Query BIT1 for max profit where price < 3: returns 7 (from price=1)
  • left[4] = 7
  • Update BIT1: position 3 gets value 8

Result: left = [0, 3, 0, 7, 7]

Step 3: Build right array (right-to-left pass)

Processing index 4 (price=3, profit=8):

  • Inverted price = 6 - 3 = 3
  • Query BIT2 for max profit where inverted price < 3: returns 0 (no items yet)
  • right[4] = 0
  • Update BIT2: position 3 gets value 8

Processing index 3 (price=5, profit=2):

  • Inverted price = 6 - 5 = 1
  • Query BIT2 for max profit where inverted price < 1: returns 0 (no valid items)
  • right[3] = 0
  • Update BIT2: position 1 gets value 2

Processing index 2 (price=1, profit=7):

  • Inverted price = 6 - 1 = 5
  • Query BIT2 for max profit where inverted price < 5: returns 8 (from original price=3)
  • right[2] = 8
  • Update BIT2: position 5 gets value 7

Processing index 1 (price=4, profit=6):

  • Inverted price = 6 - 4 = 2
  • Query BIT2 for max profit where inverted price < 2: returns 2 (from original price=5)
  • right[1] = 2
  • Update BIT2: position 2 gets value 6

Processing index 0 (price=2, profit=3):

  • Inverted price = 6 - 2 = 4
  • Query BIT2 for max profit where inverted price < 4: returns 8 (best among original prices 3,4,5)
  • right[0] = 8
  • Update BIT2: position 4 gets value 3

Result: right = [8, 2, 8, 0, 0]

Step 4: Calculate maximum profit

For each position as middle element:

  • Position 0: left[0]=0, invalid (no item with smaller price on left)
  • Position 1: left[1]=3, profits[1]=6, right[1]=2 โ†’ Total = 3+6+2 = 11
  • Position 2: left[2]=0, invalid (no item with smaller price on left)
  • Position 3: left[3]=7, profits[3]=2, right[3]=0, invalid (no item with larger price on right)
  • Position 4: left[4]=7, profits[4]=8, right[4]=0, invalid (no item with larger price on right)

Maximum profit = 11 (selecting items at indices 0, 1, 3 with prices 2 < 4 < 5 and profits 3+6+2=11)

Actually, let me recalculate the right array more carefully. When processing right-to-left, we want items with prices greater than the current price. The inverted price trick helps us find these.

After careful recalculation, the valid triplet would be indices (2, 4, 3) with prices (1, 3, 5) giving profits 7+8+2=17, or indices (0, 1, 3) with prices (2, 4, 5) giving profits 3+6+2=11. The maximum would be 17.

Solution Implementation

1class BinaryIndexedTree:
2    """
3    A Binary Indexed Tree (Fenwick Tree) modified to support range maximum queries.
4    Used to efficiently find the maximum value in a range.
5    """
6  
7    def __init__(self, n: int):
8        """
9        Initialize the Binary Indexed Tree.
10      
11        Args:
12            n: The size of the tree (maximum index value)
13        """
14        self.size = n
15        self.tree = [0] * (n + 1)  # 1-indexed array for BIT operations
16
17    def update(self, index: int, value: int):
18        """
19        Update the tree with a new value at the given index.
20        Maintains the maximum value at each node.
21      
22        Args:
23            index: The position to update (1-indexed)
24            value: The value to update with (keeps maximum)
25        """
26        while index <= self.size:
27            self.tree[index] = max(self.tree[index], value)
28            # Move to next index affected by this update
29            # Adding the lowest set bit moves to parent in BIT
30            index += index & -index
31
32    def query(self, index: int) -> int:
33        """
34        Query the maximum value from index 1 to the given index.
35      
36        Args:
37            index: The upper bound of the range query (1-indexed)
38          
39        Returns:
40            The maximum value in range [1, index]
41        """
42        max_value = 0
43        while index > 0:
44            max_value = max(max_value, self.tree[index])
45            # Move to previous range by removing the lowest set bit
46            index -= index & -index
47        return max_value
48
49
50class Solution:
51    def maxProfit(self, prices: List[int], profits: List[int]) -> int:
52        """
53        Find the maximum profit from selecting three items i < j < k
54        where prices[i] < prices[j] < prices[k].
55      
56        Args:
57            prices: List of item prices
58            profits: List of corresponding profits for each item
59          
60        Returns:
61            Maximum sum of profits[i] + profits[j] + profits[k] where the
62            price constraint is satisfied, or -1 if no valid triplet exists
63        """
64        n = len(prices)
65      
66        # Arrays to store maximum profit from valid items to the left and right
67        max_profit_left = [0] * n   # Max profit from items with smaller price to the left
68        max_profit_right = [0] * n  # Max profit from items with larger price to the right
69
70        # Find the maximum price to determine BIT size
71        max_price = max(prices)
72      
73        # Two BITs: one for left-to-right scan, one for right-to-left scan
74        left_tree = BinaryIndexedTree(max_price + 1)
75        right_tree = BinaryIndexedTree(max_price + 1)
76
77        # First pass: Find maximum profit from items with smaller prices to the left
78        for i, current_price in enumerate(prices):
79            # Query for maximum profit among all items with price < current_price
80            max_profit_left[i] = left_tree.query(current_price - 1)
81            # Update tree with current item's profit at its price position
82            left_tree.update(current_price, profits[i])
83      
84        # Second pass: Find maximum profit from items with larger prices to the right
85        for i in range(n - 1, -1, -1):
86            # Transform price to handle reverse ordering (larger prices query)
87            # We want items with price > current_price, so we reverse the scale
88            reversed_price = max_price + 1 - prices[i]
89            # Query for maximum profit among all items with price > current_price
90            max_profit_right[i] = right_tree.query(reversed_price - 1)
91            # Update tree with current item's profit at its reversed price position
92            right_tree.update(reversed_price, profits[i])
93
94        # Find the maximum sum where both left and right have valid items (non-zero profit)
95        # For each middle item j, calculate: profit_left[j] + profit[j] + profit_right[j]
96        return max(
97            (left_profit + middle_profit + right_profit 
98             for left_profit, middle_profit, right_profit in zip(max_profit_left, profits, max_profit_right) 
99             if left_profit and right_profit),  # Ensure valid triplet exists
100            default=-1  # Return -1 if no valid triplet found
101        )
102
1/**
2 * Binary Indexed Tree (Fenwick Tree) for range maximum queries
3 * This implementation supports finding the maximum value in a prefix range
4 */
5class BinaryIndexedTree {
6    private int size;           // Size of the tree
7    private int[] tree;         // Tree array storing maximum values
8  
9    /**
10     * Constructor to initialize the Binary Indexed Tree
11     * @param size The size of the tree
12     */
13    public BinaryIndexedTree(int size) {
14        this.size = size;
15        this.tree = new int[size + 1];  // 1-indexed array
16    }
17  
18    /**
19     * Updates the tree by propagating the maximum value upwards
20     * @param index The position to update (1-indexed)
21     * @param value The value to potentially update with
22     */
23    public void update(int index, int value) {
24        // Traverse upwards in the tree using the last set bit
25        while (index <= size) {
26            tree[index] = Math.max(tree[index], value);
27            index += index & -index;  // Add the last set bit to move to parent
28        }
29    }
30  
31    /**
32     * Queries the maximum value in the range [1, index]
33     * @param index The end position of the query range (1-indexed)
34     * @return The maximum value in the range
35     */
36    public int query(int index) {
37        int maxValue = 0;
38        // Traverse downwards in the tree
39        while (index > 0) {
40            maxValue = Math.max(maxValue, tree[index]);
41            index -= index & -index;  // Remove the last set bit to move to previous range
42        }
43        return maxValue;
44    }
45}
46
47/**
48 * Solution class for finding maximum profit from selecting exactly 3 items
49 * where prices[i] < prices[j] < prices[k] for indices i < j < k
50 */
51class Solution {
52    /**
53     * Finds the maximum profit by selecting 3 items with strictly increasing prices
54     * @param prices Array of item prices
55     * @param profits Array of item profits
56     * @return Maximum total profit, or -1 if no valid triplet exists
57     */
58    public int maxProfit(int[] prices, int[] profits) {
59        int itemCount = prices.length;
60        int[] maxProfitLeft = new int[itemCount];   // Max profit from left with smaller price
61        int[] maxProfitRight = new int[itemCount];  // Max profit from right with larger price
62      
63        // Find the maximum price to determine tree size
64        int maxPrice = 0;
65        for (int price : prices) {
66            maxPrice = Math.max(maxPrice, price);
67        }
68      
69        // Tree for finding max profit among items with smaller prices on the left
70        BinaryIndexedTree leftTree = new BinaryIndexedTree(maxPrice + 1);
71      
72        // Tree for finding max profit among items with larger prices on the right
73        BinaryIndexedTree rightTree = new BinaryIndexedTree(maxPrice + 1);
74      
75        // Build left array: for each position, find max profit from items on left with smaller price
76        for (int i = 0; i < itemCount; i++) {
77            int currentPrice = prices[i];
78            // Query for max profit among all items with price < currentPrice
79            maxProfitLeft[i] = leftTree.query(currentPrice - 1);
80            // Update tree with current item's profit at its price position
81            leftTree.update(currentPrice, profits[i]);
82        }
83      
84        // Build right array: for each position, find max profit from items on right with larger price
85        for (int i = itemCount - 1; i >= 0; i--) {
86            // Transform price to handle "greater than" queries using BIT
87            // We want items with price > prices[i], so we use inverted index
88            int invertedPrice = maxPrice + 1 - prices[i];
89            // Query for max profit among all items with price > currentPrice
90            maxProfitRight[i] = rightTree.query(invertedPrice - 1);
91            // Update tree with current item's profit at its inverted price position
92            rightTree.update(invertedPrice, profits[i]);
93        }
94      
95        // Find the maximum total profit by considering each item as the middle element
96        int maxTotalProfit = -1;
97        for (int i = 0; i < itemCount; i++) {
98            // Check if current item can be the middle of a valid triplet
99            if (maxProfitLeft[i] > 0 && maxProfitRight[i] > 0) {
100                int totalProfit = maxProfitLeft[i] + profits[i] + maxProfitRight[i];
101                maxTotalProfit = Math.max(maxTotalProfit, totalProfit);
102            }
103        }
104      
105        return maxTotalProfit;
106    }
107}
108
1class BinaryIndexedTree {
2private:
3    int size;                    // Size of the tree (1-indexed)
4    vector<int> tree;            // Binary indexed tree array
5
6public:
7    // Constructor: Initialize BIT with given size
8    BinaryIndexedTree(int n) {
9        this->size = n;
10        tree.resize(n + 1, 0);   // 1-indexed array, initialized to 0
11    }
12
13    // Update the tree with maximum value at position x
14    void update(int x, int value) {
15        // Propagate the update to all affected nodes
16        while (x <= size) {
17            tree[x] = max(tree[x], value);  // Store maximum value
18            x += x & -x;                    // Move to next node using lowbit
19        }
20    }
21
22    // Query the maximum value in range [1, x]
23    int query(int x) {
24        int maxValue = 0;
25        // Traverse the tree to find maximum
26        while (x > 0) {
27            maxValue = max(maxValue, tree[x]);
28            x -= x & -x;                    // Move to parent node using lowbit
29        }
30        return maxValue;
31    }
32};
33
34class Solution {
35public:
36    int maxProfit(vector<int>& prices, vector<int>& profits) {
37        int n = prices.size();
38        vector<int> leftMaxProfit(n, 0);   // Max profit from left side for each position
39        vector<int> rightMaxProfit(n, 0);  // Max profit from right side for each position
40      
41        // Find the maximum price to determine BIT size
42        int maxPrice = *max_element(prices.begin(), prices.end());
43      
44        // Create two BITs for left and right processing
45        BinaryIndexedTree leftTree(maxPrice + 1);
46        BinaryIndexedTree rightTree(maxPrice + 1);
47      
48        // Process from left to right: find max profit with smaller price on the left
49        for (int i = 0; i < n; ++i) {
50            int currentPrice = prices[i];
51            // Query maximum profit from all prices less than current price
52            leftMaxProfit[i] = leftTree.query(currentPrice - 1);
53            // Update tree with current profit at current price
54            leftTree.update(currentPrice, profits[i]);
55        }
56      
57        // Process from right to left: find max profit with larger price on the right
58        for (int i = n - 1; i >= 0; --i) {
59            // Transform price to handle "greater than" queries using BIT
60            int transformedPrice = maxPrice + 1 - prices[i];
61            // Query maximum profit from all prices greater than current price
62            rightMaxProfit[i] = rightTree.query(transformedPrice - 1);
63            // Update tree with current profit at transformed price
64            rightTree.update(transformedPrice, profits[i]);
65        }
66      
67        // Find the maximum sum of three profits where middle element satisfies conditions
68        int maxTotalProfit = -1;
69        for (int i = 0; i < n; ++i) {
70            // Check if there exists valid left and right elements
71            if (leftMaxProfit[i] > 0 && rightMaxProfit[i] > 0) {
72                // Calculate total profit: left + middle + right
73                int totalProfit = leftMaxProfit[i] + profits[i] + rightMaxProfit[i];
74                maxTotalProfit = max(maxTotalProfit, totalProfit);
75            }
76        }
77      
78        return maxTotalProfit;
79    }
80};
81
1// Binary Indexed Tree (Fenwick Tree) for range maximum queries
2// Array to store the tree nodes
3let n: number;
4let c: number[];
5
6// Initialize the Binary Indexed Tree with size n
7function initBIT(size: number): void {
8    n = size;
9    c = Array(n + 1).fill(0);
10}
11
12// Update the tree at position x with maximum value v
13// Uses the BIT update pattern: x += x & -x to traverse parent nodes
14function update(x: number, v: number): void {
15    while (x <= n) {
16        c[x] = Math.max(c[x], v);
17        x += x & -x;  // Move to next position using lowbit
18    }
19}
20
21// Query the maximum value in range [1, x]
22// Uses the BIT query pattern: x -= x & -x to traverse the tree
23function query(x: number): number {
24    let maxValue = 0;
25    while (x > 0) {
26        maxValue = Math.max(maxValue, c[x]);
27        x -= x & -x;  // Move to parent using lowbit
28    }
29    return maxValue;
30}
31
32// Find maximum profit from selecting exactly 3 items with strictly increasing prices
33function maxProfit(prices: number[], profits: number[]): number {
34    const length: number = prices.length;
35  
36    // Arrays to store maximum profit to the left and right of each index
37    const leftMaxProfit: number[] = Array(length).fill(0);
38    const rightMaxProfit: number[] = Array(length).fill(0);
39  
40    // Find the maximum price to determine BIT size
41    const maxPrice = Math.max(...prices);
42  
43    // First pass: Calculate maximum profit for items with smaller price to the left
44    initBIT(maxPrice + 1);
45    for (let i = 0; i < length; i++) {
46        const currentPrice: number = prices[i];
47        // Query maximum profit from items with price < currentPrice
48        leftMaxProfit[i] = query(currentPrice - 1);
49        // Update tree with current item's profit
50        update(currentPrice, profits[i]);
51    }
52  
53    // Second pass: Calculate maximum profit for items with larger price to the right
54    initBIT(maxPrice + 1);
55    for (let i = length - 1; i >= 0; i--) {
56        // Transform price to handle reverse order (larger prices)
57        const transformedPrice: number = maxPrice + 1 - prices[i];
58        // Query maximum profit from items with price > currentPrice
59        rightMaxProfit[i] = query(transformedPrice - 1);
60        // Update tree with current item's profit
61        update(transformedPrice, profits[i]);
62    }
63  
64    // Find the maximum total profit by considering each item as the middle element
65    let maxTotalProfit: number = -1;
66    for (let i = 0; i < length; i++) {
67        // Check if current item can be middle (has valid left and right items)
68        if (leftMaxProfit[i] > 0 && rightMaxProfit[i] > 0) {
69            const totalProfit = leftMaxProfit[i] + profits[i] + rightMaxProfit[i];
70            maxTotalProfit = Math.max(maxTotalProfit, totalProfit);
71        }
72    }
73  
74    return maxTotalProfit;
75}
76

Time and Space Complexity

Time Complexity: O(n ร— log M)

The algorithm uses two Binary Indexed Trees (Fenwick Trees) to track maximum profits. For each of the n elements in the prices array:

  • In the first loop: For each price, we perform a query operation (which takes O(log M) time) and an update operation (which also takes O(log M) time)
  • In the second loop: Similarly, for each price, we perform a query and an update operation, each taking O(log M) time

The Binary Indexed Tree operations (query and update) have time complexity of O(log M) because:

  • In update: we traverse up the tree by adding x & -x (the lowest set bit), which takes at most log M steps
  • In query: we traverse down by subtracting x & -x, which also takes at most log M steps

Since we process n elements and perform O(log M) operations for each element, the total time complexity is O(n ร— log M).

Space Complexity: O(M)

The space is dominated by:

  • Two Binary Indexed Trees, each using an array of size M + 1: O(M)
  • Two auxiliary arrays left and right of size n: O(n)

Since M represents the maximum value in the prices array (which is at most 5000 in this problem) and typically M could be larger than n, the overall space complexity is O(M).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Handling of Duplicate Prices

One of the most common pitfalls is not properly handling duplicate prices. The problem requires strictly increasing prices (prices[i] < prices[j] < prices[k]), but the Binary Indexed Tree implementation might accidentally include items with equal prices.

The Issue: When querying tree.query(x - 1), we get the maximum profit from all items with prices from 1 to x-1. This correctly excludes items with price x, ensuring strict inequality. However, if multiple items have the same price, the BIT update order matters.

Example Problem Scenario:

prices = [3, 3, 5]
profits = [1, 2, 3]

If we're not careful, when processing the second item (price=3), we might include the first item (also price=3) in our left array, violating the strict inequality requirement.

The Solution in the Code: The implementation correctly handles this by:

  • Using query(current_price - 1) which excludes items with price equal to current_price
  • Processing items sequentially, so when we query for item j, we only see items i < j in the tree

2. Off-by-One Errors in BIT Indexing

Binary Indexed Trees typically use 1-based indexing, but prices might be 0 or negative in the general case.

The Issue: If prices contain 0 or if we don't handle the indexing correctly, we might get runtime errors or incorrect results.

Example Problem:

prices = [0, 1, 2]  # Price of 0 would cause issues
profits = [5, 3, 4]

The Solution: The current implementation assumes all prices are positive integers. To handle zero or negative prices, you would need to:

# Add offset to ensure all prices are positive
min_price = min(prices)
offset = 1 - min_price if min_price <= 0 else 0
adjusted_prices = [p + offset for p in prices]

3. Memory Limit with Large Price Values

The BIT size is determined by the maximum price value, not the number of items.

The Issue: If prices can be very large (e.g., prices = [1, 2, 1000000000]), the BIT would need to allocate arrays of size 1000000001, causing memory issues.

The Solution: Use coordinate compression to map prices to a smaller range:

# Coordinate compression
unique_prices = sorted(set(prices))
price_map = {price: i + 1 for i, price in enumerate(unique_prices)}
compressed_prices = [price_map[p] for p in prices]
max_compressed = len(unique_prices)

4. Confusion with the Right Array Price Inversion

The trickiest part of the solution is understanding why we use m + 1 - prices[i] for the right array.

The Issue: Developers might try to directly query for prices greater than the current price, which doesn't work with the standard BIT maximum query that finds max in range [1, x].

Why the Inversion Works:

  • We want to find items with price > current_price to the right
  • BIT naturally finds maximum in range [1, x] (items with price โ‰ค x)
  • By inverting prices: large prices become small inverted values
  • Now querying for inverted values less than m + 1 - current_price gives us original prices greater than current_price

Example:

# Original prices: [1, 3, 2, 4]
# Max price m = 4
# Inverted prices: [4, 2, 3, 1]
# For price=2, inverted=3, query(2) finds items with inverted price โ‰ค 2
# This corresponds to original prices โ‰ฅ 3

5. Edge Case: Arrays with Less Than 3 Elements

The implementation handles this correctly by returning -1 when no valid triplet exists, but it's worth noting.

The Issue: With fewer than 3 elements, no valid triplet can exist.

The Solution (already handled): The max(..., default=-1) ensures -1 is returned when no valid triplet is found, which naturally handles the case when n < 3.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these properties could exist for a graph but not a tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More