Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions problems/3867-sum-of-gcd-of-formed-pairs/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# 3867. Sum of GCD of Formed Pairs

[LeetCode Link](https://leetcode.com/problems/sum-of-gcd-of-formed-pairs/)

Difficulty: Medium
Topics: Array, Math, Two Pointers, Sorting, Simulation, Number Theory
Acceptance Rate: 69.6%

## Hints

### Hint 1

The problem reads like a chain of separate steps: build one array, sort it, pair
things up, then reduce. Don't try to be clever about merging the steps — this is a
simulation problem. Handle each stage faithfully and the pieces fall into place. The
only questions are: how do I build `prefixGcd` efficiently, and how do I pair the
sorted elements without an expensive nested loop?

### Hint 2

For `prefixGcd`, notice that `mxi` is just a running (prefix) maximum. You can compute
the whole array in one pass while carrying the max seen so far. For the pairing step,
"smallest unpaired with largest unpaired" after sorting is the textbook signal for the
**two-pointers** technique: one pointer at the front, one at the back, walking inward.

### Hint 3

Once `prefixGcd` is sorted, set `left = 0` and `right = n - 1`. While `left < right`,
add `gcd(prefixGcd[left], prefixGcd[right])` to the answer, then move `left++` and
`right--`. The loop condition `left < right` handles the odd-length case for free: the
single middle element is exactly where the two pointers meet, so it is never paired and
never counted. No special-casing required.

## Approach

The task is a direct simulation with two efficiency-sensitive stages.

**Stage 1 — Build `prefixGcd`.** For each index `i`, `mxi` is the maximum of
`nums[0..i]`, which is a prefix maximum. Keep a variable `mx` that we update to
`max(mx, nums[i])` as we scan left to right, and set
`prefixGcd[i] = gcd(nums[i], mx)`. This is a single `O(n)` pass (each `gcd` is
`O(log V)`), avoiding any `O(n^2)` recomputation of the max.

For example, with `nums = [3, 6, 2, 8]`:

- `i=0`: `mx=3`, `gcd(3,3)=3`
- `i=1`: `mx=6`, `gcd(6,6)=6`
- `i=2`: `mx=6`, `gcd(2,6)=2`
- `i=3`: `mx=8`, `gcd(8,8)=8`

So `prefixGcd = [3, 6, 2, 8]`.

**Stage 2 — Sort.** Sort `prefixGcd` in non-decreasing order: `[2, 3, 6, 8]`.

**Stage 3 — Pair with two pointers.** The rule "pair the smallest unpaired with the
largest unpaired, repeatedly" is exactly a two-pointer sweep from both ends of the
sorted array. Start `left = 0`, `right = n - 1`:

- `left=0, right=3`: `gcd(2, 8) = 2`
- `left=1, right=2`: `gcd(3, 6) = 3`
- `left=2, right=1`: `left < right` fails, stop.

Sum `= 2 + 3 = 5`, matching the expected output.

When `n` is odd, the two pointers eventually satisfy `left == right` on the middle
element, and the `left < right` guard stops the loop before it is used — so the middle
element is naturally ignored, as the problem requires.

Computing `gcd` uses the Euclidean algorithm. Since values can be up to `10^9`, the sum
of GCDs across up to ~`5 * 10^4` pairs can exceed a 32-bit range in the worst case
(each GCD up to `10^9`), so accumulate the answer in a 64-bit integer to be safe.

## Complexity Analysis

Time Complexity: O(n log n) — the sort dominates; building `prefixGcd` is `O(n log V)`
and the two-pointer pass is `O(n log V)`, where `V` is the maximum value.
Space Complexity: O(n) — for the `prefixGcd` array (or O(1) auxiliary beyond it,
ignoring the sort's internal usage).

## Edge Cases

- **Single element (`n == 1`):** `prefixGcd` has one element; no pair can be formed and
the answer is `0`. The two-pointer loop never executes since `left < right` is false
immediately.
- **Odd length:** The middle element after sorting must be skipped. The `left < right`
condition handles this automatically — no explicit middle-index arithmetic needed.
- **All equal values:** e.g. `[5, 5, 5, 5]` gives `prefixGcd = [5, 5, 5, 5]`, and each
pair contributes `gcd(5, 5) = 5`. A good sanity check that pairing/summing works.
- **Large values (up to `10^9`) and many pairs:** Accumulate the sum in a 64-bit
integer to avoid overflow, since the total can exceed the 32-bit range.
- **Strictly increasing input:** `mxi == nums[i]` at every index, so
`prefixGcd[i] = gcd(nums[i], nums[i]) = nums[i]`; a useful case to confirm the prefix
max is tracked correctly.
95 changes: 95 additions & 0 deletions problems/3867-sum-of-gcd-of-formed-pairs/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
number: "3867"
frontend_id: "3867"
title: "Sum of GCD of Formed Pairs"
slug: "sum-of-gcd-of-formed-pairs"
difficulty: "Medium"
topics:
- "Array"
- "Math"
- "Two Pointers"
- "Sorting"
- "Simulation"
- "Number Theory"
acceptance_rate: 6958.0
is_premium: false
created_at: "2026-07-16T03:45:53.859780+00:00"
fetched_at: "2026-07-16T03:45:53.859780+00:00"
link: "https://leetcode.com/problems/sum-of-gcd-of-formed-pairs/"
date: "2026-07-16"
---

# 3867. Sum of GCD of Formed Pairs

You are given an integer array `nums` of length `n`.

Construct an array `prefixGcd` where for each index `i`:

* Let `mxi = max(nums[0], nums[1], ..., nums[i])`.
* `prefixGcd[i] = gcd(nums[i], mxi)`.



After constructing `prefixGcd`:

* Sort `prefixGcd` in **non-decreasing** order.
* Form pairs by taking the **smallest unpaired** element and the **largest unpaired** element.
* Repeat this process until no more pairs can be formed.
* For each formed pair, **compute** the `gcd` of the two elements.
* If `n` is odd, the **middle** element in the `prefixGcd` array remains **unpaired** and should be ignored.



Return an integer denoting the **sum of the GCD** values of all formed pairs.

The term `gcd(a, b)` denotes the **greatest common divisor** of `a` and `b`.



**Example 1:**

**Input:** nums = [2,6,4]

**Output:** 2

**Explanation:**

Construct `prefixGcd`:

`i` | `nums[i]` | `mxi` | `prefixGcd[i]`
---|---|---|---
0 | 2 | 2 | 2
1 | 6 | 6 | 6
2 | 4 | 6 | 2

`prefixGcd = [2, 6, 2]`. After sorting, it forms `[2, 2, 6]`.

Pair the smallest and largest elements: `gcd(2, 6) = 2`. The remaining middle element 2 is ignored. Thus, the sum is 2.

**Example 2:**

**Input:** nums = [3,6,2,8]

**Output:** 5

**Explanation:**

Construct `prefixGcd`:

`i` | `nums[i]` | `mxi` | `prefixGcd[i]`
---|---|---|---
0 | 3 | 3 | 3
1 | 6 | 6 | 6
2 | 2 | 6 | 2
3 | 8 | 8 | 8

`prefixGcd = [3, 6, 2, 8]`. After sorting, it forms `[2, 3, 6, 8]`.

Form pairs: `gcd(2, 8) = 2` and `gcd(3, 6) = 3`. Thus, the sum is `2 + 3 = 5`.



**Constraints:**

* `1 <= n == nums.length <= 105`
* `1 <= nums[i] <= 10​​​​​​​9`
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import "sort"

// Approach: Simulate the three stages directly.
// 1. Build prefixGcd in one pass using a running prefix maximum:
// prefixGcd[i] = gcd(nums[i], max(nums[0..i])).
// 2. Sort prefixGcd in non-decreasing order.
// 3. Pair the smallest unpaired with the largest unpaired using two pointers
// (left at the front, right at the back) and sum gcd(left, right) for each
// pair. The `left < right` guard leaves the middle element unpaired when n
// is odd, exactly as required.
//
// The answer is accumulated in an int64 to avoid overflow, since values can be
// up to 1e9 across many pairs.

// gcd returns the greatest common divisor of a and b using the Euclidean
// algorithm. Assumes non-negative inputs (constraints guarantee nums[i] >= 1).
func gcd(a, b int) int {
for b != 0 {
a, b = b, a%b
}
return a
}

func sumGcd(nums []int) int64 {
n := len(nums)

// Stage 1: build prefixGcd with a running maximum.
prefixGcd := make([]int, n)
mx := 0
for i, v := range nums {
if v > mx {
mx = v
}
prefixGcd[i] = gcd(v, mx)
}

// Stage 2: sort in non-decreasing order.
sort.Ints(prefixGcd)

// Stage 3: two-pointer pairing from both ends.
var sum int64
for left, right := 0, n-1; left < right; left, right = left+1, right-1 {
sum += int64(gcd(prefixGcd[left], prefixGcd[right]))
}
return sum
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import "testing"

func TestSumGcd(t *testing.T) {
tests := []struct {
name string
nums []int
expected int64
}{
{
name: "example 1: [2,6,4] -> sorted [2,2,6], gcd(2,6)=2",
nums: []int{2, 6, 4},
expected: 2,
},
{
name: "example 2: [3,6,2,8] -> sorted [2,3,6,8], gcd(2,8)+gcd(3,6)=5",
nums: []int{3, 6, 2, 8},
expected: 5,
},
{
name: "edge case: single element has no pairs",
nums: []int{7},
expected: 0,
},
{
name: "edge case: two identical elements, gcd(5,5)=5",
nums: []int{5, 5},
expected: 5,
},
{
name: "edge case: odd length middle ignored",
nums: []int{5, 5, 5, 5, 5},
expected: 10, // sorted [5,5,5,5,5]: gcd(5,5)+gcd(5,5)=10, middle skipped
},
{
name: "edge case: strictly increasing keeps values as prefixGcd",
nums: []int{1, 2, 3, 4},
expected: 2, // prefixGcd [1,2,3,4]: gcd(1,4)+gcd(2,3)=1+1=2
},
{
name: "edge case: large values, no overflow",
nums: []int{1000000000, 1000000000},
expected: 1000000000, // gcd(1e9,1e9)=1e9
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := sumGcd(tt.nums)
if result != tt.expected {
t.Errorf("sumGcd(%v) = %d, want %d", tt.nums, result, tt.expected)
}
})
}
}