Skip to main content
Back to problems
#3691
Hard Algorithms

Maximum total subarray value ii

Array Greedy Segment Tree Heap (Priority Queue)
22.0% acceptance
Feb 25, 2026
80
4
You are given an integer array nums of length n and an integer k. You must select exactly k distinct non-empty subarrays nums[l..r] of nums. Subarrays may overlap, but the exact same subarray (same l and r) cannot be chosen more than once. The value of a subarray nums[l..r] is defined as: max(nums[l..r]) - min(nums[l..r]). The total value is the sum of the values of all chosen subarrays. Return the maximum possible total value you can achieve.

Solution

Rust
Time O(n²)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn max_total_value(nums: Vec<i32>, k: i32) -> i64 {
    // Optimized O(n * log(max_val)) algorithm:
    //
    // Binary search for v* = the k-th largest subarray value. For a given threshold v:
    //   count_le(v)  = # subarrays with (max - min) <= v  [O(n) sliding window]
    //   sum_le(v)    = sum of (max-min) for those subarrays [O(n) augmented deques]
    //
    // After finding v*:
    //   count_gt = total - count_le(v*)          [# subarrays strictly above v*]
    //   sum_gt   = sum_all - sum_le(v*)           [their total value]
    //   answer   = sum_gt + v* * (k - count_gt)
    //
    // count_le / sum_le use two monotonic deques (one for max, one for min).
    // Each deque stores (index, span-from-right); running sums updated in O(1) amortized.

    let n = nums.len() as i64;
    let k = k as i64;
    let total = n * (n + 1) / 2;

    if nums.is_empty() {
      return 0;
    }

    // --- helper: sliding-window count + sum of subarrays with max-min <= threshold ---
    let count_and_sum = |threshold: i64| -> (i64, i64) {
      let m = nums.len();
      // Deques store indices; max_deq is non-increasing in value, min_deq non-decreasing.
      // sum_max = sum over l in [left,right] of max([l,right])
      // sum_min = sum over l in [left,right] of min([l,right])
      let mut max_deq: std::collections::VecDeque<usize> = std::collections::VecDeque::new();
      let mut min_deq: std::collections::VecDeque<usize> = std::collections::VecDeque::new();
      let mut sum_max: i64 = 0;
      let mut sum_min: i64 = 0;
      let mut cnt: i64 = 0;
      let mut val_sum: i64 = 0;
      let mut left = 0usize;

      for right in 0..m {
        let rv = nums[right] as i64;

        // Extend max_deq: pop back while nums[back] <= rv, then push right.
        // Each popped entry back covered l in [prev+1, back]; remove its contribution.
        while !max_deq.is_empty() && nums[*max_deq.back().unwrap()] as i64 <= rv {
          let back = max_deq.pop_back().unwrap();
          let prev = max_deq.back().copied().map_or(left as i64 - 1, |x| x as i64);
          sum_max -= nums[back] as i64 * (back as i64 - prev);
        }
        let prev = max_deq.back().copied().map_or(left as i64 - 1, |x| x as i64);
        sum_max += rv * (right as i64 - prev);
        max_deq.push_back(right);

        // Extend min_deq: pop back while nums[back] >= rv, then push right.
        while !min_deq.is_empty() && nums[*min_deq.back().unwrap()] as i64 >= rv {
          let back = min_deq.pop_back().unwrap();
          let prev = min_deq.back().copied().map_or(left as i64 - 1, |x| x as i64);
          sum_min -= nums[back] as i64 * (back as i64 - prev);
        }
        let prev = min_deq.back().copied().map_or(left as i64 - 1, |x| x as i64);
        sum_min += rv * (right as i64 - prev);
        min_deq.push_back(right);

        // Shrink from left while window violates max - min > threshold.
        // Removing l=left: subtract its max and min contributions; pop front if stale.
        while nums[*max_deq.front().unwrap()] as i64
          - nums[*min_deq.front().unwrap()] as i64
          > threshold
        {
          sum_max -= nums[*max_deq.front().unwrap()] as i64;
          if *max_deq.front().unwrap() == left {
            max_deq.pop_front();
          }
          sum_min -= nums[*min_deq.front().unwrap()] as i64;
          if *min_deq.front().unwrap() == left {
            min_deq.pop_front();
          }
          left += 1;
        }

        let window = (right - left + 1) as i64;
        cnt += window;
        val_sum += sum_max - sum_min;
      }
      (cnt, val_sum)
    };

    // Binary search for smallest v* with count_le(v*) > total - k.
    let (_, sum_all) = {
      // compute sum_all via count_and_sum at max possible threshold (captures all subarrays)
      let max_val = *nums.iter().max().unwrap() as i64;
      let min_val = *nums.iter().min().unwrap() as i64;
      count_and_sum(max_val - min_val)
    };

    let need = total - k; // count_le(v*) must be > need → count_le(v*) >= need + 1

    let max_threshold = *nums.iter().max().unwrap() as i64 - *nums.iter().min().unwrap() as i64;
    let mut lo: i64 = 0;
    let mut hi: i64 = max_threshold;
    while lo < hi {
      let mid = lo + (hi - lo) / 2;
      let (c, _) = count_and_sum(mid);
      if c > need {
        hi = mid;
      } else {
        lo = mid + 1;
      }
    }
    let v_star = lo;

    let (count_le_vstar, sum_le_vstar) = count_and_sum(v_star);
    let count_gt = total - count_le_vstar;
    let sum_gt = sum_all - sum_le_vstar;

    sum_gt + v_star * (k - count_gt)
  }
}