diff --git a/problems/3312-sorted-gcd-pair-queries/analysis.md b/problems/3312-sorted-gcd-pair-queries/analysis.md new file mode 100644 index 0000000..15be82f --- /dev/null +++ b/problems/3312-sorted-gcd-pair-queries/analysis.md @@ -0,0 +1,62 @@ +# 3312. Sorted GCD Pair Queries + +[LeetCode Link](https://leetcode.com/problems/sorted-gcd-pair-queries/) + +Difficulty: Hard +Topics: Array, Hash Table, Math, Binary Search, Combinatorics, Counting, Number Theory, Prefix Sum +Acceptance Rate: 34.5% + +## Hints + +### Hint 1 + +There can be up to `n * (n - 1) / 2 ≈ 5 * 10^9` pairs, so you can never actually build the `gcdPairs` array. But notice that every GCD value is bounded: `1 <= gcd <= max(nums) <= 5 * 10^4`. That is a much smaller universe than the number of pairs. What can you count over that small range instead of enumerating pairs? + +### Hint 2 + +Instead of asking "what is the gcd of each pair", flip it around: for each candidate value `g`, ask "how many pairs have gcd *exactly* `g`?" If you had that count for every `g`, the sorted `gcdPairs` array is just each `g` repeated that many times — and a prefix sum over `g` lets you answer any query with binary search. This is a classic **counting + number theory** setup. + +### Hint 3 + +Counting pairs with gcd *exactly* `g` directly is hard, but counting pairs where both elements are *divisible by* `g` is easy: if `m` numbers are multiples of `g`, there are `C(m, 2) = m*(m-1)/2` such pairs. That count includes pairs whose gcd is `g`, `2g`, `3g`, ... So process `g` from large to small (or subtract as you go): `exact[g] = pairsDivisibleBy[g] - exact[2g] - exact[3g] - ...`. This inclusion–exclusion sieve is the crux. Then prefix-sum `exact[]` and binary-search each query. + +## Approach + +**Step 1 — Frequency table.** Let `M = max(nums)`. Build `cnt[v]` = how many elements of `nums` equal `v`, for `1 <= v <= M`. + +**Step 2 — Count pairs divisible by each `g`.** For every `g` from `1` to `M`, sum `cnt[g] + cnt[2g] + cnt[3g] + ...` to get `m`, the number of array elements that are multiples of `g`. The number of pairs where *both* elements are multiples of `g` is `pairs[g] = m*(m-1)/2`. This harmonic-style double loop costs `O(M log M)`. + +**Step 3 — Inclusion–exclusion to get exact gcd counts.** `pairs[g]` counts every pair whose gcd is a multiple of `g`. To isolate pairs with gcd *exactly* `g`, subtract the exact counts of all larger multiples: + +``` +exactCount[g] = pairs[g] - exactCount[2g] - exactCount[3g] - ... +``` + +Process `g` from `M` down to `1` so that `exactCount[2g], exactCount[3g], ...` are already finalized when you compute `exactCount[g]`. (You can also fold Steps 2 and 3 into one downward loop.) + +**Step 4 — Prefix sums.** Build `prefix[g] = exactCount[1] + exactCount[2] + ... + exactCount[g]`. `prefix[g]` is the number of pairs whose gcd is `<= g`, i.e. how many entries of the conceptual sorted `gcdPairs` array are `<= g`. + +**Step 5 — Answer queries.** For a query index `q` (0-based), the answer is the smallest `g` such that `prefix[g] > q`. Binary search over `g` in `[1, M]` finds it in `O(log M)`. + +**Why it works.** The sorted `gcdPairs` array places all `exactCount[1]` ones first, then `exactCount[2]` twos, and so on. The prefix sum tells you exactly where each value's block ends, so locating the `q`-th element is a binary search on those block boundaries — no need to materialize the (possibly billions-long) array. + +**Tiny example.** `nums = [2,3,4]`, `M = 4`, `cnt = [_,0,1,1,1]`. +- `pairs[1]`: multiples of 1 → 3 numbers → `C(3,2)=3`. +- `pairs[2]`: multiples of 2 → {2,4} → 2 numbers → `C(2,2)=1`. +- `pairs[3]`: {3} → 1 number → 0. `pairs[4]`: {4} → 0. +- Going down: `exact[4]=0`, `exact[3]=0`, `exact[2]=1-0=1`, `exact[1]=3-1=2`. +- `prefix = [_,2,3,3,3]`. Query 0 → smallest g with prefix>0 → g=1. Query 2 → smallest g with prefix>2 → g=2. Matches `[1,2,2]`. + +## Complexity Analysis + +Time Complexity: O(n + M log M + Q log M), where `n = len(nums)`, `M = max(nums)`, and `Q = len(queries)`. The `M log M` term comes from the harmonic sieve summing multiples; each query is a binary search. + +Space Complexity: O(M) for the frequency, exact-count, and prefix arrays. + +## Edge Cases + +- **Only one distinct pair** (`n = 2`): `gcdPairs` has a single element; every query must return that element. Covered by the general prefix-sum logic. +- **Duplicate values** (e.g. `nums = [2,2]`): `gcd(2,2) = 2`, and `cnt[2] = 2` yields `C(2,2)=1` pair — duplicates must be counted, not deduplicated. +- **All numbers coprime**: most pairs collapse to gcd `1`, so `exactCount[1]` dominates; the prefix sum still resolves queries correctly. +- **Large query indices**: `queries[i]` can be up to `n*(n-1)/2 ≈ 5*10^9`, which overflows 32-bit integers. Use 64-bit (`int` on 64-bit Go, or `int64`) for query values, `m*(m-1)/2`, and prefix sums. +- **`max(nums)` sizing**: allocate arrays up to `M` inclusive; off-by-one on the upper bound drops the largest possible gcd. diff --git a/problems/3312-sorted-gcd-pair-queries/problem.md b/problems/3312-sorted-gcd-pair-queries/problem.md new file mode 100644 index 0000000..3e42bdc --- /dev/null +++ b/problems/3312-sorted-gcd-pair-queries/problem.md @@ -0,0 +1,79 @@ +--- +number: "3312" +frontend_id: "3312" +title: "Sorted GCD Pair Queries" +slug: "sorted-gcd-pair-queries" +difficulty: "Hard" +topics: + - "Array" + - "Hash Table" + - "Math" + - "Binary Search" + - "Combinatorics" + - "Counting" + - "Number Theory" + - "Prefix Sum" +acceptance_rate: 3451.3 +is_premium: false +created_at: "2026-07-17T03:45:42.308459+00:00" +fetched_at: "2026-07-17T03:45:42.308459+00:00" +link: "https://leetcode.com/problems/sorted-gcd-pair-queries/" +date: "2026-07-17" +--- + +# 3312. Sorted GCD Pair Queries + +You are given an integer array `nums` of length `n` and an integer array `queries`. + +Let `gcdPairs` denote an array obtained by calculating the GCD of all possible pairs `(nums[i], nums[j])`, where `0 <= i < j < n`, and then sorting these values in **ascending** order. + +For each query `queries[i]`, you need to find the element at index `queries[i]` in `gcdPairs`. + +Return an integer array `answer`, where `answer[i]` is the value at `gcdPairs[queries[i]]` for each query. + +The term `gcd(a, b)` denotes the **greatest common divisor** of `a` and `b`. + + + +**Example 1:** + +**Input:** nums = [2,3,4], queries = [0,2,2] + +**Output:** [1,2,2] + +**Explanation:** + +`gcdPairs = [gcd(nums[0], nums[1]), gcd(nums[0], nums[2]), gcd(nums[1], nums[2])] = [1, 2, 1]`. + +After sorting in ascending order, `gcdPairs = [1, 1, 2]`. + +So, the answer is `[gcdPairs[queries[0]], gcdPairs[queries[1]], gcdPairs[queries[2]]] = [1, 2, 2]`. + +**Example 2:** + +**Input:** nums = [4,4,2,1], queries = [5,3,1,0] + +**Output:** [4,2,1,1] + +**Explanation:** + +`gcdPairs` sorted in ascending order is `[1, 1, 1, 2, 2, 4]`. + +**Example 3:** + +**Input:** nums = [2,2], queries = [0,0] + +**Output:** [2,2] + +**Explanation:** + +`gcdPairs = [2]`. + + + +**Constraints:** + + * `2 <= n == nums.length <= 105` + * `1 <= nums[i] <= 5 * 104` + * `1 <= queries.length <= 105` + * `0 <= queries[i] < n * (n - 1) / 2` diff --git a/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717.go b/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717.go new file mode 100644 index 0000000..2e706a9 --- /dev/null +++ b/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717.go @@ -0,0 +1,70 @@ +package main + +// 3312. Sorted GCD Pair Queries +// +// Approach: counting + number-theory sieve + prefix sum + binary search. +// +// We never build the (up to ~5e9 long) gcdPairs array. Instead we count, for +// every value g in [1, M] where M = max(nums), how many pairs have gcd exactly g. +// +// 1. cnt[v] = frequency of value v in nums. +// 2. For each g, m = number of array elements divisible by g (sum cnt[g], +// cnt[2g], ...). Pairs with both elements divisible by g = m*(m-1)/2. This +// over-counts: it includes pairs whose gcd is 2g, 3g, ... +// 3. Sweep g from M down to 1 and subtract the already-finalized exact counts of +// larger multiples: exact[g] = pairsDivisibleBy(g) - exact[2g] - exact[3g] - ... +// 4. prefix[g] = number of pairs with gcd <= g (cumulative sum of exact). +// 5. Each query index q maps to the smallest g with prefix[g] > q (binary search). +// +// Time : O(n + M log M + Q log M) +// Space : O(M) +func gcdValues(nums []int, queries []int64) []int { + maxVal := 0 + for _, v := range nums { + if v > maxVal { + maxVal = v + } + } + + cnt := make([]int, maxVal+1) + for _, v := range nums { + cnt[v]++ + } + + // exact[g] will hold the number of pairs whose gcd is exactly g. + exact := make([]int64, maxVal+1) + for g := maxVal; g >= 1; g-- { + // m = count of array elements that are multiples of g. + var m int64 + var over int64 // pairs already claimed by larger multiples of g + for mult := g; mult <= maxVal; mult += g { + m += int64(cnt[mult]) + if mult > g { + over += exact[mult] + } + } + exact[g] = m*(m-1)/2 - over + } + + // prefix[g] = number of pairs with gcd <= g. Length maxVal+1, prefix[0] = 0. + prefix := make([]int64, maxVal+1) + for g := 1; g <= maxVal; g++ { + prefix[g] = prefix[g-1] + exact[g] + } + + answer := make([]int, len(queries)) + for i, q := range queries { + // Smallest g in [1, maxVal] such that prefix[g] > q. + lo, hi := 1, maxVal + for lo < hi { + mid := (lo + hi) / 2 + if prefix[mid] > q { + hi = mid + } else { + lo = mid + 1 + } + } + answer[i] = lo + } + return answer +} diff --git a/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717_test.go b/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717_test.go new file mode 100644 index 0000000..44813ea --- /dev/null +++ b/problems/3312-sorted-gcd-pair-queries/solution_daily_20260717_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "reflect" + "testing" +) + +func TestSolution(t *testing.T) { + tests := []struct { + name string + nums []int + queries []int64 + expected []int + }{ + { + name: "example 1: [2,3,4] queries [0,2,2]", + nums: []int{2, 3, 4}, + queries: []int64{0, 2, 2}, + expected: []int{1, 2, 2}, + }, + { + name: "example 2: [4,4,2,1] queries [5,3,1,0]", + nums: []int{4, 4, 2, 1}, + queries: []int64{5, 3, 1, 0}, + expected: []int{4, 2, 1, 1}, + }, + { + name: "example 3: [2,2] queries [0,0]", + nums: []int{2, 2}, + queries: []int64{0, 0}, + expected: []int{2, 2}, + }, + { + name: "edge case: single pair coprime", + nums: []int{3, 5}, + queries: []int64{0}, + expected: []int{1}, + }, + { + name: "edge case: all duplicates yield same gcd", + nums: []int{6, 6, 6}, + queries: []int64{0, 1, 2}, + expected: []int{6, 6, 6}, + }, + { + name: "edge case: full sorted gcdPairs scanned in order", + // nums = [4,4,2,1] -> gcdPairs sorted = [1,1,1,2,2,4] + nums: []int{4, 4, 2, 1}, + queries: []int64{0, 1, 2, 3, 4, 5}, + expected: []int{1, 1, 1, 2, 2, 4}, + }, + { + name: "edge case: mixed values with larger gcds", + // nums = [10,15,20] -> gcd(10,15)=5, gcd(10,20)=10, gcd(15,20)=5 + // sorted = [5,5,10] + nums: []int{10, 15, 20}, + queries: []int64{0, 1, 2}, + expected: []int{5, 5, 10}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := gcdValues(tt.nums, tt.queries) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("gcdValues(%v, %v) = %v, want %v", tt.nums, tt.queries, result, tt.expected) + } + }) + } +}