Leetcode 3356. Zero Array Transformation II

#Array #Binary Search #Prefix Sum

Table of Contents

Problem Informations

Problem Description

You are given an integer array nums of length $n$ and a 2D array queries where $queries[i] = [l_i, r_i, val_i]$.

Each queries[i] represents the following action on nums:

  • Decrement the value at each index in the range $[l_i, r_i]$ in nums by at most $val_i$.
  • The amount by which each value is decremented can be chosen independently for each index.

A Zero Array is an array with all its elements equal to 0.

Return the minimum possible non-negative value of $k$, such that after processing the first $k$ queries in sequence, nums becomes a Zero Array. If no such $k$ exists, return -1.

Example 1:

Input: nums = [2,0,2], queries = [[0,2,1],[0,2,1],[1,1,3]]

Output: 2

Explanation:

  • For i = 0 (l = 0, r = 2, val = 1):

    • Decrement values at indices [0, 1, 2] by [1, 0, 1] respectively.
    • The array will become [1, 0, 1].
  • For i = 1 (l = 0, r = 2, val = 1):

    • Decrement values at indices [0, 1, 2] by [1, 0, 1] respectively.
    • The array will become [0, 0, 0], which is a Zero Array. Therefore, the minimum value of $k$ is 2.

Example 2:

Input: nums = [4,3,2,1], queries = [[1,3,2],[0,2,1]]

Output: -1

Explanation:

  • For i = 0 (l = 1, r = 3, val = 2):

    • Decrement values at indices [1, 2, 3] by [2, 2, 1] respectively.
    • The array will become [4, 1, 0, 0].
  • For i = 1 (l = 0, r = 2, val = 1):

    • Decrement values at indices [0, 1, 2] by [1, 1, 0] respectively.
    • The array will become [3, 0, 0, 0], which is not a Zero Array.

Constraints:

  • $1 \leq nums.length \leq 10^5$
  • $0 \leq nums[i] \leq 5 \times 10^5$
  • $1 \leq queries.length \leq 10^5$
  • $queries[i].length == 3$
  • $0 \leq l_i \leq r_i < nums.length$
  • $1 \leq val_i \leq 5$

Intuition

The problem requires us to determine the minimum number of queries needed to transform the integer array nums into a zero array by decrementing its values based on the given query operations. Each query can independently reduce values at specific indices, but only to a limited extent. A careful observation reveals that we can leverage a difference array technique to efficiently apply these range updates and quickly check if the transformations are feasible within each query limit. This leads to the idea of employing a binary search over the number of queries to identify the smallest number of queries needed to achieve a zero array.

Approach

  1. Difference Array Technique:

    • We use a difference array diff of size n + 1 to facilitate range updates in constant time. This technique helps in applying multiple range update operations effectively.
  2. Lambda Function for Validation:

    • We define a lambda function canZeroOut(mid) which assesses whether the first mid queries can transform nums into a zero array.
    • For a given mid, iterate over the queries up to mid and update the diff array by incrementing at the start index and decrementing at endIndex + 1. This marks the beginning and end of an operation range.
  3. Checking the Feasibility:

    • Utilize a variable currentSum which tracks the cumulative decrement effect applied to each element of nums.
    • Iterate through the nums array, updating currentSum with values from diff. If at any index the cumulative decrement (currentSum) is less than the corresponding nums element, return false as it’s impossible to zero out that element with given mid.
    • If feasible for all indices, return true.
  4. Binary Search:

    • Apply binary search over mid, starting from 0 up to m + 1, where m is the number of queries.
    • The binary search is employed to efficiently find the smallest value of mid such that the first mid queries can zero out the array.
    • Adjust the search range based on the feasibility check: if canZeroOut(mid) is true, explore smaller mid; otherwise, increase mid.
  5. Result Evaluation:

    • If the binary search result low surpasses m, it indicates no valid sequence of queries can zero out nums, hence return -1.
    • Otherwise, return low as it signifies the minimum number of queries needed.

The entire approach revolves around efficiently handling range operations and leveraging binary search to minimize computational overhead using the constraints given.

Code

C++

class Solution {
public:
    int minZeroArray(vector<int>& nums, vector<vector<int>>& queries) {
        int m = queries.size(); // Number of queries
        int n = nums.size();    // Length of the nums array

        // Lambda function to determine if the first 'mid' queries can zero out the array
        auto canZeroOut = [&](int mid) -> bool {
            vector<long long> diff(n + 1, 0); // Difference array to efficiently apply range updates

            // Apply the first 'mid' queries to the difference array
            for (int i = 0; i < mid; i++) {
                int startIndex = queries[i][0];
                int endIndex = queries[i][1];
                int val = queries[i][2];

                diff[startIndex] += val;
                if (endIndex + 1 < n) {
                    diff[endIndex + 1] -= val;
                }
            }

            long long currentSum = 0;
            // Check if, after applying these updates, all elements in nums are zeroed
            for (int j = 0; j < n; j++) {
                currentSum += diff[j];
                // If the current cumulative decrement doesn't zero the element, return false
                if (currentSum < nums[j]) {
                    return false;
                }
            }
            return true;
        };

        // Binary search to find the minimum number of queries that can zero out the array
        int low = 0, high = m + 1; // Start with one more than m to handle the case where no valid k exists
        while (low < high) {
            int mid = low + (high - low) / 2;
            // If the first 'mid' queries can zero out the array, search for a smaller 'mid'
            if (canZeroOut(mid)) {
                high = mid;
            } else { // Otherwise, search for a larger 'mid'
                low = mid + 1;
            }
        }

        // If low is greater than the number of queries, it means no valid k was found
        return (low > m) ? -1 : low;
    }
};

Python

class Solution:
    def minZeroArray(self, nums, queries):
        m = len(queries)  # Number of queries
        n = len(nums)     # Length of the nums array

        # Lambda function to determine if the first 'mid' queries can zero out the array
        def canZeroOut(mid):
            diff = [0] * (n + 1)  # Difference array to efficiently apply range updates

            # Apply the first 'mid' queries to the difference array
            for i in range(mid):
                startIndex = queries[i][0]
                endIndex = queries[i][1]
                val = queries[i][2]

                diff[startIndex] += val
                if endIndex + 1 < n:
                    diff[endIndex + 1] -= val

            currentSum = 0
            # Check if, after applying these updates, all elements in nums are zeroed
            for j in range(n):
                currentSum += diff[j]
                # If the current cumulative decrement doesn't zero the element, return false
                if currentSum < nums[j]:
                    return False
            return True

        # Binary search to find the minimum number of queries that can zero out the array
        low, high = 0, m + 1  # Start with one more than m to handle the case where no valid k exists
        while low < high:
            mid = low + (high - low) // 2
            # If the first 'mid' queries can zero out the array, search for a smaller 'mid'
            if canZeroOut(mid):
                high = mid
            else:  # Otherwise, search for a larger 'mid'
                low = mid + 1

        # If low is greater than the number of queries, it means no valid k was found
        return -1 if low > m else low

Complexity

  • Time complexity: $O((n + m) \log m)$

    The time complexity of the solution involves two main components:

    1. Binary Search over Queries: The binary search algorithm attempts to find the minimum number of queries $k$ needed to zero out nums. The binary search runs in $O(\log m)$ time, where $m$ is the number of queries.

    2. Range Update and Validation: For each check during the binary search, the algorithm uses a range update technique based on a difference array, which requires $O(n)$ time to apply k queries on the nums array.

    Thus, for each iteration in the binary search, we perform $O(n)$ operations, resulting in an overall time complexity of $O((n + m) \log m)$.

  • Space complexity: $O(n)$

    The space complexity arises mainly from the use of a difference array diff of size $n + 1$. This array is used to perform efficient range updates when checking if a given set of queries can zero out nums. Hence, the space complexity is $O(n)$.

Disclaimer: All reference materials on this website are sourced from the internet and are intended for learning purposes only. If you believe any content infringes upon your rights, please contact me at csnote.cc@gmail.com, and I will remove the relevant content promptly.


Feedback Welcome: If you notice any errors or areas for improvement in the articles, I warmly welcome your feedback and corrections. Your input will help this blog provide better learning resources. This is an ongoing process of learning and improvement, and your suggestions are valuable to me. You can reach me at csnote.cc@gmail.com.