Skip to main content
Back to problems
#3049
Hard Algorithms

Earliest second to mark indices ii

Array Binary Search Greedy Heap (Priority Queue)
22.4% acceptance
Feb 25, 2026
83
20
You are given two 1-indexed integer arrays, nums and, changeIndices, having lengths n and m, respectively. Initially, all indices in nums are unmarked. Your task is to mark all indices in nums. In each second, s, in order from 1 to m (inclusive), you can perform one of the following operations: Choose an index i in the range [1, n] and decrement nums[i] by 1. Set nums[changeIndices[s]] to any non-negative value. Choose an index i in the range [1, n], where nums[i] is equal to 0, and mark index i. Do nothing. Return an integer denoting the earliest second in the range [1, m] when all indices in nums can be marked by choosing operations optimally, or -1 if it is impossible.

Solution

Rust
Time O(n log n)
Space O(n)
LeetCode
solution.rs
use std::collections::BinaryHeap;
use std::cmp::Reverse;

impl Solution {
  pub fn earliest_second_to_mark_indices(nums: Vec<i32>, change_indices: Vec<i32>) -> i32 {
    let n = nums.len();
    let m = change_indices.len();

    // Key insight: if we fast-mark index i (use set to zero it then mark it),
    // it is optimal to use the FIRST occurrence of i in changeIndices for the set-op.
    // This frees all later occurrences as regular free seconds.
    //
    // Binary search on budget t. For a given t, check feasibility:
    //   1. Compute first_occ[i] = first index in changeIndices[0..t) where changeIndices[s] == i,
    //      for indices i where nums[i] > 0.
    //   2. Scan right-to-left. Maintain a min-heap of nums[i] for committed fast-marks,
    //      and a "mark" counter for available free mark-slots.
    //   3. At first_occ second: push nums[i] to heap, then:
    //        if mark > 0 (have a free slot): mark -= 1 (this second used for set, not free)
    //        else: we need to discard the least-valuable fast-mark (heap.pop()), mark += 1.
    //             (this second itself acts as the free slot for the popped fast-mark's mark)
    //   4. At regular second: mark += 1.
    //   5. Final check: sum(nums) - sum(pq) + n - pq.len() <= mark.
    //      (ops remaining = decrements_for_non_fast_marked + marks_for_all = total_ops - fast_mark_savings)

    let can = |t: usize| -> bool {
      // Find first occurrence of each index i (1-indexed) in changeIndices[0..t) where nums[i]>0
      let mut first_occ = vec![-1i32; n + 1];
      for s in 0..t {
        let idx = change_indices[s] as usize;
        if first_occ[idx] == -1 && nums[idx - 1] > 0 {
          first_occ[idx] = s as i32;
        }
      }
      // Map from position back to index
      let mut pos_to_idx = vec![0usize; t];
      for i in 1..=n {
        if first_occ[i] >= 0 {
          pos_to_idx[first_occ[i] as usize] = i;
        }
      }

      let mut heap: BinaryHeap<Reverse<i64>> = BinaryHeap::new();
      let mut mark: i64 = 0;

      for s in (0..t).rev() {
        let i = pos_to_idx[s];
        if i > 0 {
          // This is the first occurrence of index i; candidate for fast-mark
          heap.push(Reverse(nums[i - 1] as i64));
          if mark > 0 {
            mark -= 1; // this second used as set-op; consume a mark slot
          } else {
            mark += 1; // this second becomes a free slot after popping worst
            heap.pop(); // remove least-valuable fast-mark
          }
        } else {
          mark += 1; // regular free second
        }
      }

      // Remaining ops: for indices NOT in heap (not fast-marked):
      //   sum(nums) - heap_sum (decrements saved by fast-marks)
      //   + n - heap.len() (marks for non-fast-marked indices)
      //   + heap.len() (marks for fast-marked indices, from free seconds)
      //   = n + sum(nums) - heap_sum - heap.len() + heap.len() = n + sum(nums) - heap_sum
      // Wait: fast-marked indices also need 1 mark second (from free pool).
      // Non-fast-marked indices need nums[i] dec-seconds + 1 mark-second.
      // Total free ops needed = (sum_nums - heap_sum) + n
      // These must fit in `mark` free seconds.
      let heap_sum: i64 = heap.iter().map(|&Reverse(v)| v).sum();
      let sum_nums: i64 = nums.iter().map(|&x| x as i64).sum();
      sum_nums - heap_sum + (n - heap.len()) as i64 <= mark
    };

    let mut lo = 1usize;
    let mut hi = m + 1;
    while lo < hi {
      let mid = (lo + hi) / 2;
      if can(mid) { hi = mid; } else { lo = mid + 1; }
    }
    if lo > m { -1 } else { lo as i32 }
  }
}