Prime Subtraction Operation

medium greedy primes binary search

Problem

You are given a 0-indexed integer array nums. Any number of times you may pick an index you haven't picked before and a prime p strictly less than nums[i], then subtract p from nums[i]. Return true if you can make nums strictly increasing, and false otherwise.

Inputnums = [4, 9, 6, 10]
Outputtrue
Subtract 3 from index 0 → 1, and 7 from index 1 → 2, giving [1, 2, 6, 10], which is strictly increasing.

def primeSubOperation(nums):
    # Sieve of Eratosthenes for all primes up to 1000.
    is_prime = [True] * 1001
    is_prime[0] = is_prime[1] = False
    for i in range(2, 32):
        if is_prime[i]:
            for j in range(i * i, 1001, i):
                is_prime[j] = False
    primes = [i for i in range(2, 1001) if is_prime[i]]

    prev = 0                       # value fixed for the previous index
    for x in nums:
        # Largest prime p < x - prev makes x - p as small as possible
        # while still > prev. bisect_left finds that prime in O(log).
        lo, hi, p = 0, len(primes) - 1, 0
        while lo <= hi:
            mid = (lo + hi) // 2
            if primes[mid] < x - prev:
                p = primes[mid]    # candidate, keep searching right
                lo = mid + 1
            else:
                hi = mid - 1
        v = x - p                  # smallest reachable value > prev
        if v <= prev:              # cannot beat the previous element
            return False
        prev = v
    return True
function primeSubOperation(nums) {
  // Sieve of Eratosthenes for all primes up to 1000.
  const isPrime = new Array(1001).fill(true);
  isPrime[0] = isPrime[1] = false;
  for (let i = 2; i < 32; i++) {
    if (isPrime[i]) {
      for (let j = i * i; j <= 1000; j += i) isPrime[j] = false;
    }
  }
  const primes = [];
  for (let i = 2; i <= 1000; i++) if (isPrime[i]) primes.push(i);

  let prev = 0;                      // value fixed for the previous index
  for (const x of nums) {
    // Largest prime p < x - prev makes x - p as small as possible
    // while still > prev. Binary search finds that prime.
    let lo = 0, hi = primes.length - 1, p = 0;
    while (lo <= hi) {
      const mid = (lo + hi) >> 1;
      if (primes[mid] < x - prev) { p = primes[mid]; lo = mid + 1; }
      else hi = mid - 1;
    }
    const v = x - p;                 // smallest reachable value > prev
    if (v <= prev) return false;      // cannot beat the previous element
    prev = v;
  }
  return true;
}
boolean primeSubOperation(int[] nums) {
    // Sieve of Eratosthenes for all primes up to 1000.
    boolean[] isPrime = new boolean[1001];
    Arrays.fill(isPrime, true);
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i < 32; i++)
        if (isPrime[i])
            for (int j = i * i; j <= 1000; j += i) isPrime[j] = false;
    List<Integer> primes = new ArrayList<>();
    for (int i = 2; i <= 1000; i++) if (isPrime[i]) primes.add(i);

    int prev = 0;                      // value fixed for the previous index
    for (int x : nums) {
        // Largest prime p < x - prev makes x - p as small as possible
        // while still > prev. Binary search finds that prime.
        int lo = 0, hi = primes.size() - 1, p = 0;
        while (lo <= hi) {
            int mid = (lo + hi) >>> 1;
            if (primes.get(mid) < x - prev) { p = primes.get(mid); lo = mid + 1; }
            else hi = mid - 1;
        }
        int v = x - p;                 // smallest reachable value > prev
        if (v <= prev) return false;    // cannot beat the previous element
        prev = v;
    }
    return true;
}
bool primeSubOperation(vector<int>& nums) {
    // Sieve of Eratosthenes for all primes up to 1000.
    vector<bool> isPrime(1001, true);
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i < 32; i++)
        if (isPrime[i])
            for (int j = i * i; j <= 1000; j += i) isPrime[j] = false;
    vector<int> primes;
    for (int i = 2; i <= 1000; i++) if (isPrime[i]) primes.push_back(i);

    int prev = 0;                      // value fixed for the previous index
    for (int x : nums) {
        // Largest prime p < x - prev makes x - p as small as possible
        // while still > prev. Binary search finds that prime.
        int lo = 0, hi = (int)primes.size() - 1, p = 0;
        while (lo <= hi) {
            int mid = (lo + hi) / 2;
            if (primes[mid] < x - prev) { p = primes[mid]; lo = mid + 1; }
            else hi = mid - 1;
        }
        int v = x - p;                 // smallest reachable value > prev
        if (v <= prev) return false;    // cannot beat the previous element
        prev = v;
    }
    return true;
}
Time: O(M log log M + n log P) Space: O(M)