Skip to main content
Back to problems
#3574
Hard Algorithms

Maximize subarray gcd score

Array Math Enumeration Number Theory
24.7% acceptance
Feb 25, 2026
46
4
You are given array nums and integer k (at most k operations, each doubles one element once). Score of subarray = length * GCD(all elements). Return maximum score.

Solution

Rust
Time O(n²)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn max_gcd_score(nums: Vec<i32>, k: i32) -> i64 {
    let n = nums.len();
    let k = k as usize;

    fn gcd(a: i64, b: i64) -> i64 {
      if b == 0 { a } else { gcd(b, a % b) }
    }

    let mut ans = 0i64;

    // For each subarray [i..j], the optimal strategy is:
    // - GCD of subarray determines the score multiplied by length
    // - Doubling an element x increases GCD only if all others share gcd with 2x
    // - Key insight: doubling element x changes gcd(subarray) only if x is the bottleneck
    //   In practice, for a subarray, we can double at most k elements.
    //   The GCD can increase by factor of 2 if we double all elements that have an odd factor.
    // 
    // Simplified observation: For any subarray, the GCD g of original values divides all nums[i].
    // Doubling nums[i] changes it to 2*nums[i]. The GCD after doubling some subset S is:
    //   gcd of {2*nums[i] for i in S, nums[i] for i not in S}
    // = gcd of {nums[i] for all i} * possible_factor if all elements in subarray are doubled
    //   or keep same g if not all are doubled.
    // Actually: if we double ALL elements: gcd becomes 2*g, score = len * 2*g
    // If we double SOME elements: gcd may or may not change.
    //
    // For subarray of length L: if L <= k, we can double all elements, gcd = 2*g, score = 2*g*L
    // If L > k: we can only double k of the L elements. Doubling k of them won't make gcd = 2*g
    //   unless each undoubled element already has factor 2.
    //
    // More precisely: gcd(subarray after doubling subset S) = gcd of the modified values.
    // Let g = gcd(nums[i..j]). Write nums[i] = g * a[i]. Then gcd(a[i]) = 1.
    // After doubling subset S: gcd = g * gcd({2*a[i] for i in S, a[i] for i not in S}).
    // Since gcd(a) = 1, and we double some subset, the resulting gcd is:
    //   If not all are doubled: gcd(2*a[i], a[j]) divides a[j], so gcd is 1 or 2.
    //   It's 2 iff all a[j] (undoubled) are even. But gcd(a[i])=1 means not all even unless...
    //   Actually: for gcd to be 2, all undoubled a[j] must be even AND all doubled 2*a[i] must be even (trivially).
    //   If some a[j] is odd and j not in S, then gcd is odd (=1). So gcd=1, score = g*L.
    //   
    // Optimal: for each subarray [i,j] of length L:
    //   g = gcd(nums[i..=j])
    //   score_no_double = L * g
    //   If L <= k: can double all -> score = 2 * L * g
    //   Else: can we double all odd-a[j] elements? Count of elements where nums[j]/g is odd.
    //     If that count <= k: score = 2 * L * g
    //     Else: score = L * g

    for i in 0..n {
      let g = 0i64;
      let mut odd_count = 0usize; // count of nums with (nums/gcd) odd
      // When gcd changes, odd_count needs to be recomputed
      // We'll track g incrementally and recompute odd_count when needed
      let mut cur_gcd = 0i64;
      for j in i..n {
        let v = nums[j] as i64;
        let prev_gcd = cur_gcd;
        cur_gcd = gcd(cur_gcd, v);
        let _ = g;
        let len = (j - i + 1) as i64;

        // Recompute odd_count when gcd changes
        if cur_gcd != prev_gcd {
          // Recompute odd_count for subarray [i..=j]
          odd_count = (i..=j).filter(|&r| (nums[r] as i64 / cur_gcd) % 2 == 1).count();
        } else {
          // Just add nums[j]: check if nums[j]/cur_gcd is odd
          if (v / cur_gcd) % 2 == 1 {
            odd_count += 1;
          }
        }

        let score = if odd_count <= k {
          2 * len * cur_gcd
        } else {
          len * cur_gcd
        };
        if score > ans { ans = score; }
      }
    }

    ans
  }
}