LeetCode #3671 — HARD

Sum of Beautiful Subsequences

Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.

Solve on LeetCode
The Problem

Problem Statement

You are given an integer array nums of length n.

For every positive integer g, we define the beauty of g as the product of g and the number of strictly increasing subsequences of nums whose greatest common divisor (GCD) is exactly g.

Return the sum of beauty values for all positive integers g.

Since the answer could be very large, return it modulo 109 + 7.

Example 1:

Input: nums = [1,2,3]

Output: 10

Explanation:

All strictly increasing subsequences and their GCDs are:

Subsequence GCD
[1] 1
[2] 2
[3] 3
[1,2] 1
[1,3] 1
[2,3] 1
[1,2,3] 1

Calculating beauty for each GCD:

GCD Count of subsequences Beauty (GCD × Count)
1 5 1 × 5 = 5
2 1 2 × 1 = 2
3 1 3 × 1 = 3

Total beauty is 5 + 2 + 3 = 10.

Example 2:

Input: nums = [4,6]

Output: 12

Explanation:

All strictly increasing subsequences and their GCDs are:

Subsequence GCD
[4] 4
[6] 6
[4,6] 2

Calculating beauty for each GCD:

GCD Count of subsequences Beauty (GCD × Count)
2 1 2 × 1 = 2
4 1 4 × 1 = 4
6 1 6 × 1 = 6

Total beauty is 2 + 4 + 6 = 12.

Constraints:

  • 1 <= n == nums.length <= 104
  • 1 <= nums[i] <= 7 * 104
Patterns Used

Roadmap

  1. Brute Force Baseline
  2. Core Insight
  3. Algorithm Walkthrough
  4. Edge Cases
  5. Full Annotated Code
  6. Interactive Study Demo
  7. Complexity Analysis
Step 01

Brute Force Baseline

Problem summary: You are given an integer array nums of length n. For every positive integer g, we define the beauty of g as the product of g and the number of strictly increasing subsequences of nums whose greatest common divisor (GCD) is exactly g. Return the sum of beauty values for all positive integers g. Since the answer could be very large, return it modulo 109 + 7.

Baseline thinking

Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.

Pattern signal: Array · Math · Segment Tree

Example 1

[1,2,3]

Example 2

[4,6]
Step 02

Core Insight

What unlocks the optimal approach

  • Fix a candidate GCD <code>g</code> and keep, in the original order, only those array elements divisible by <code>g</code>; scale them down to <code>x / g</code> so any increasing subsequence here corresponds to a subsequence whose elements are all multiples of <code>g</code>.
  • Count strictly increasing subsequences of that scaled list by assigning ranks (coordinate compression) and maintaining prefix sums of ways for smaller ranks (you may use a Fenwick tree).
  • The count you get, call it <code>cnt_g</code>, includes subsequences whose GCD is <code>g</code> or any multiple of <code>g</code>; it is therefore an overcount for "exactly g".
  • To get the number with GCD exactly <code>g</code>, process <code>g</code> from <code>max(nums)</code> down to <code>1</code> and subtract counts already assigned to multiples: <code>F[g] = cnt_g - sum{k=2g,3g,...}*F[k]</code> (do arithmetic mod <code>MOD</code>); descending order ensures multiples are known.
  • The final answer is the sum of contributions <code>g * F[g]</code> for all <code>g</code>.
Interview move: turn each hint into an invariant you can check after every iteration/recursion step.
Step 03

Algorithm Walkthrough

Iteration Checklist

  1. Define state (indices, window, stack, map, DP cell, or recursion frame).
  2. Apply one transition step and update the invariant.
  3. Record answer candidate when condition is met.
  4. Continue until all input is consumed.
Use the first example testcase as your mental trace to verify each transition.
Step 04

Edge Cases

Minimum Input
Single element / shortest valid input
Validate boundary behavior before entering the main loop or recursion.
Duplicates & Repeats
Repeated values / repeated states
Decide whether duplicates should be merged, skipped, or counted explicitly.
Extreme Constraints
Largest constraint values
Re-check complexity target against constraints to avoid time-limit issues.
Invalid / Corner Shape
Empty collections, zeros, or disconnected structures
Handle special-case structure before the core algorithm path.
Step 05

Full Annotated Code

Source-backed implementations are provided below for direct study and interview prep.

// Accepted solution for LeetCode #3671: Sum of Beautiful Subsequences
// Auto-generated Java example from go.
class Solution {
    public void exampleSolution() {
    }
}
// Reference (go):
// // Accepted solution for LeetCode #3671: Sum of Beautiful Subsequences
// package main
// 
// import "slices"
// 
// // https://space.bilibili.com/206214
// const mod = 1_000_000_007
// const mx = 70_001
// 
// var divisors [mx][]int
// 
// func init() {
// 	// 预处理每个数的因子
// 	for i := 1; i < mx; i++ {
// 		for j := i; j < mx; j += i { // 枚举 i 的倍数 j
// 			divisors[j] = append(divisors[j], i) // i 是 j 的因子
// 		}
// 	}
// }
// 
// // 完整模板见 https://leetcode.cn/circle/discuss/mOr1u6/
// type fenwick []int
// 
// func newFenwickTree(n int) fenwick {
// 	return make(fenwick, n+1) // 使用下标 1 到 n
// }
// 
// // a[i] 增加 val
// // 1 <= i <= n
// // 时间复杂度 O(log n)
// func (f fenwick) update(i, val int) {
// 	for ; i < len(f); i += i & -i {
// 		f[i] += val
// 	}
// }
// 
// // 求前缀和 a[1] + ... + a[i]
// // 1 <= i <= n
// // 时间复杂度 O(log n)
// func (f fenwick) pre(i int) (res int) {
// 	for ; i > 0; i &= i - 1 {
// 		res += f[i]
// 	}
// 	return res % mod
// }
// 
// func totalBeauty(nums []int) (ans int) {
// 	m := slices.Max(nums)
// 
// 	// 计算 b 的严格递增子序列的个数
// 	countIncreasingSubsequence := func(b []int, g int) (res int) {
// 		t := newFenwickTree(m / g)
// 		for _, x := range b {
// 			x /= g
// 			// cnt 表示以 x 结尾的严格递增子序列的个数
// 			cnt := t.pre(x-1) + 1 // +1 是因为 x 可以一个数组成一个子序列
// 			res += cnt
// 			t.update(x, cnt) // 更新以 x 结尾的严格递增子序列的个数
// 		}
// 		return res % mod
// 	}
// 
// 	groups := make([][]int, m+1)
// 	for _, x := range nums {
// 		for _, d := range divisors[x] {
// 			groups[d] = append(groups[d], x)
// 		}
// 	}
// 
// 	f := make([]int, m+1)
// 	for i := m; i > 0; i-- {
// 		f[i] = countIncreasingSubsequence(groups[i], i)
// 		// 倍数容斥
// 		for j := i * 2; j <= m; j += i {
// 			f[i] -= f[j]
// 		}
// 		// 注意 |f[i]| * i < mod * (m / i) * i = mod * m
// 		// m 个 mod * m 相加,至多为 mod * m * m,不会超过 64 位整数最大值
// 		ans += f[i] * i
// 	}
// 	// 保证结果非负
// 	return (ans%mod + mod) % mod
// }
Step 06

Interactive Study Demo

Use this to step through a reusable interview workflow for this problem.

Press Step or Run All to begin.
Step 07

Complexity Analysis

Time
O(n + q log n)
Space
O(n)

Approach Breakdown

BRUTE FORCE
O(n × q) time
O(1) space

For each of q queries, scan the entire range to compute the aggregate (sum, min, max). Each range scan takes up to O(n) for a full-array query. With q queries: O(n × q) total. Point updates are O(1) but queries dominate.

SEGMENT TREE
O(n + q log n) time
O(n) space

Building the tree is O(n). Each query or update traverses O(log n) nodes (tree height). For q queries: O(n + q log n) total. Space is O(4n) ≈ O(n) for the tree array. Lazy propagation adds O(1) per node for deferred updates.

Shortcut: Build O(n), query/update O(log n) each. When you need both range queries AND point updates.
Coach Notes

Common Mistakes

Review these before coding to avoid predictable interview regressions.

Off-by-one on range boundaries

Wrong move: Loop endpoints miss first/last candidate.

Usually fails on: Fails on minimal arrays and exact-boundary answers.

Fix: Re-derive loops from inclusive/exclusive ranges before coding.

Overflow in intermediate arithmetic

Wrong move: Temporary multiplications exceed integer bounds.

Usually fails on: Large inputs wrap around unexpectedly.

Fix: Use wider types, modular arithmetic, or rearranged operations.