LeetCode: 327. The number of interval sums in reverse order / multiset / binary search + binary insertion

Given an integer array nums, return the interval and the number between [lower, upper], including lower and upper.
The interval and S (i, j) represent the sum of the elements in the position from i to j in nums, including i and j (i ≤ j).

Note: The
most intuitive algorithm complexity is O (n2), please optimize your algorithm on this basis.

Example:
Input: nums = [-2,5, -1], lower = -2, upper = 2,
Output: 3
Explanation: The three intervals are: [0,0], [2,2], [0 , 2], and their sums are: -2, -1, 2.

Source: LeetCode Link: https://leetcode-cn.com/problems/count-of-range-sum The
copyright belongs to the deduction network. Please contact the official authorization for commercial reprint, and please indicate the source for non-commercial reprint.

Ideas

Binary search / insert

We maintain an ordered array that represents the current known interval sum, and then each time we advance the right endpoint, we get a new prefix sum, because the interval sum is sum[r] - sum[l-1], the target becomes:

  • Knowing the right end point prefix and sr of the current interval, find all the left end points sl so that they satisfy lower<= sr - sl <=upper

Then there is a range of sl: [sr-upper, sr-lower]
in a known array that stores sl values ​​in ascending order, binary search:
find the first i such that sl is greater than or equal to sr-upper
find the first j such that sl is greater than sr-lower
ji is the current sr ( Right endpoint) can be composed of the number of pairs in reverse order

Then add sr to the sl array (the insertion position must also be searched by binary search), ready to prepare for the next sr

Sorry not to be
Insert picture description here

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper)
    {
        vector<long long> sumArr{0};
        long long ans=0, sum=0;
        for(int i=0; i<nums.size(); i++)
        {
            sum += nums[i];
            auto l = lower_bound(sumArr.begin(), sumArr.end(), sum-upper);
            auto r = upper_bound(sumArr.begin(), sumArr.end(), sum-lower);
            ans += r-l;
            auto x = lower_bound(sumArr.begin(), sumArr.end(), sum);
            sumArr.insert(x, sum);
        }
        return (int)ans;
    }
};

Introduce multiset optimization to prevent degradation

Because the worst-case binary search array is maintained, the search may degenerate to O (n) instead of stable O (log (n)). In order to maintain stable O (log (n)) , we need to use ** red Black tree (multiset) ** replaces the previous ordered array and performs binary search

Finally AC
Insert picture description here

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper)
    {
        multiset<long long> sumSet{0};
        long long ans=0, sum=0;
        for(int i=0; i<nums.size(); i++)
        {
            sum += nums[i];
            auto l = sumSet.lower_bound(sum-upper);
            auto r = sumSet.upper_bound(sum-lower);
            ans += distance(l, r);
            sumSet.insert(sum);
        }
        return (int)ans;
    }
};

Positive solution: reverse pair

In fact, in the prefix and array nums, find i, j such that i <j and lower<= nums[j] - nums[i] <= upperhow many pairs are satisfied . If you look closely, it is a bit like [ reverse order pair problem ]. This kind of finding a pair of numbers meets a certain size. Relationship (A [i] + bias <or> A [j])

Determine the size relationship
, that is, find nums [i] to satisfynums[j]-upper <= nums[i] <= nums[j]-lower

Dismantling question is:
find nums [i1] makes nums[j]-upper <= nums[i1]
looking nums [i2] makesnums[i2] <= nums[j]-lower

The problem translates into:
if the array is split into two halves, each half is in ascending order (after the merge and recursive call), determined on the lefti1, i2

  • Find the first i1 such that nums[i1] < nums[j]-upper(because of the increment, the right side of i1 is all nums[i1] >= nums[j]-upperlegal, and both i1 and its left are illegal)
  • Find the first i2 such that nums[i2] <= nums[j]-lower(i2 and its left are legal)

i2-i1 is the answer to meet the two boundaries, as shown in the figure:
Insert picture description here

Note that
enumeration j is indexed from large to small, because it needs to be in ascending order nums[i]< nums[j]. If enumeration j is from small to large, because i does not go back , then nums [j] becomes larger, which may cause the previous i to iterate forward. Some nums [i] become available.

class Solution {
public:
    int mer(vector<long long>& nums, int l, int r, int lower, int upper)
    {
        if(l>=r || l<0 || r>=nums.size()) return 0;
        int mid=(l+r)/2, cnt=0, i1=mid, i2=mid;
        cnt += mer(nums, l, mid, lower, upper)+mer(nums, mid+1, r, lower, upper);
        for(int j=r; j>mid; j--)
        {
            while(i1>=l && nums[i1]>=nums[j]-upper) i1--;
            while(i2>=l && nums[i2]>nums[j]-lower) i2--;
            cnt += i2-i1;
        }
        inplace_merge(nums.begin()+l, nums.begin()+mid+1, nums.begin()+r+1);
        return cnt;
    }
    int countRangeSum(vector<int>& nums, int lower, int upper)
    {
        vector<long long> NUMS(nums.size()+1); NUMS[0]=0;
        for(int i=0; i<nums.size(); i++)
            NUMS[i+1]=nums[i]+NUMS[i];
        return mer(NUMS, 0, NUMS.size()-1, lower, upper);
    }
};
Published 262 original articles · won 11 · 10 thousand views

Guess you like

Origin blog.csdn.net/weixin_44176696/article/details/105092920