LeetCode Entry

1508. Range Sum of Sorted Subarray Sums

04.08.2024 medium 2024 kotlin rust

Sum of [left..right] of sorted subarray's sums

1508. Range Sum of Sorted Subarray Sums meidum blog post substack youtube 1.webp

Join me on Telegram

https://t.me/leetcode_daily_unstoppable/692

Problem TLDR

Sum of [left..right] of sorted subarray’s sums #medium #heap

Intuition

Let’s look at the subarrays:

    // 3 2 4 100
    // 3
    //   2
    //     4
    //       100
    // 3 2
    //   2 4
    //     4 100
    // 3 2 4
    //   2 4 100
    // 3 2 4 100

Each of them formed as an iteration from some index. Let’s put all the iterators into a PriorityQueue and always take the smallest. This can take up to n^2 steps, as right can be n^2.

Another solution is from lee215’ & voturbac’: given the y = f(x) value we can in a linear time count how many items are lower than y. As f() grows with x we can use the binary search to find an x. The result then will be f(right) - f(left). To find the lower items count in a linear time, we should prepare the prefixes of the subarray’s sums: b[i] = num[0..i].sum() and as we summing up those subarrays, go deeper: c[i] = b[0..i].sum(). Then, there is a pattern to find the subarray sum with two pointers: move the lower bound until out of condition, then the sum will be (i - j) * your_value. The solution will be O(nlog(n)) and it takes 1ms in Rust compared to 18ms heap solution.

Approach

As n^2 accepted, let’s implement the heap solution. In Rust the BinaryHeap is a max-heap, in Kotin - min-heap

Complexity

  • Time complexity: \(O(n^2log(n))\)

  • Space complexity: \(O(n^2)\)

Code


    fun rangeSum(nums: IntArray, n: Int, left: Int, right: Int): Int {
        val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
        for (i in nums.indices) pq += nums[i] to i
        return (1..right).fold(0) { res, j ->
            val (sum, i) = pq.poll()
            if (i < nums.lastIndex) pq += (sum + nums[i + 1]) to (i + 1)
            if (j < left) 0 else (res + sum) % 1_000_000_007
        }
    }


// 18 ms

    pub fn range_sum(nums: Vec<i32>, n: i32, left: i32, right: i32) -> i32 {
        let mut bh = BinaryHeap::new();
        for i in 0..nums.len() { bh.push((-nums[i], i)) }
        (1..=right).fold(0, |res, j| {
            let (sum, i) = bh.pop().unwrap();
            if i < nums.len() - 1 { bh.push((sum - nums[i + 1], i + 1)) }
            if j < left { 0 } else { (res - sum) % 1_000_000_007 }
        })
    }


// lee215' + votrubac' = 1ms

    pub fn range_sum(nums: Vec<i32>, n: i32, left: i32, right: i32) -> i32 {
        let n = n as usize; let mut b = vec![0; n + 1]; let mut c = b.clone();
        for i in 0..n { b[i + 1] = b[i] + nums[i]; c[i + 1] = c[i] + b[i + 1] }
        fn sum_k_sums(b: &[i32], c: &[i32], k: i32, n: usize) -> i32 {
            let (mut l, mut r) = (0, b[n]);
            let mut max_score = 0;
            while l <= r {
                let m = l + (r - l) / 2;
                let (mut i, mut cnt) = (0, 0);
                for j in 0..n+1 {
                    while b[j] - b[i] > m { i += 1 }
                    cnt += (j - i) as i32
                }
                if cnt < k { l = m + 1; max_score = max_score.max(m) }  else { r = m - 1 }
            }
            let (mut res, mut i, mut cnt, mut score) = (0, 0, 0, max_score + 1);
            for j in 0..n+1 {
                while b[j] - b[i] > score { i += 1 }
                res += b[j] * (j as i32 - i as i32 + 1) - (c[j] - (if i > 0 { c[i - 1] } else { 0 }));
                res = res % 1_000_000_007;
                cnt += (j - i) as i32
            }
            res - (cnt - k) * score
        }
        sum_k_sums(&b, &c, right, n) - sum_k_sums(&b, &c, left - 1, n)
    }