题目链接2894. 分类求和并作差 - 力扣(LeetCode)
一、题目描述
给定两个正整数 n
和 m
,计算 1
到 n
中所有 不能被 m
整除的数的和 与 能被 m
整除的数的和 的差。
二、解题思路
1. 问题分析
- 目标:将
1
到n
的数分为两类:能被m
整除(记作集合A
)和不能被m
整除(记作集合B
)。 - 核心公式:所求差值为
sum(B) - sum(A)
。 - 关键观察:
sum(A) + sum(B) = sum(1..n)
,因此sum(B) - sum(A) = (sum(1..n) - 2 * sum(A))
。
2. 数学优化
- 计算
sum(1..n)
:利用等差数列求和公式,直接得到sum_total = n * (n + 1) / 2
。 - 计算
sum(A)
:- 能被
m
整除的数构成等差数列:首项a1 = m
,末项ak = k * m
(其中k
是满足k * m ≤ n
的最大整数)。 - 项数
k = n / m
(整数除法,向下取整)。 - 等差数列求和公式:
sum_A = k * (a1 + ak) / 2 = k * m * (k + 1) / 2
。
- 能被
三、代码实现(C++)
原始解法(遍历法)
class Solution {
public:
int differenceOfSums(int n, int m) {
int num1 = 0; // 不能被m整除的数的和
int num2 = 0; // 能被m整除的数的和
for (int i = 1; i <= n; i++) {
if (i % m != 0) {
num1 += i;
} else {
num2 += i;
}
}
return num1 - num2;
}
};
复杂度分析:
- 时间复杂度:
O(n)
,需遍历1
到n
的所有数。 - 空间复杂度:
O(1)
,仅使用常数级变量。
优化解法(数学公式法)
cpp
运行
class Solution {
public:
int differenceOfSums(int n, int m) {
// 计算总和:1+2+...+n
long sum_total = n * (n + 1) / 2;
// 计算能被m整除的数的个数k
int k = n / m;
// 计算能被m整除的数的和:m + 2m + ... + km = m*(1+2+...+k) = m*k*(k+1)/2
long sum_A = m * k * (k + 1) / 2;
// 差值为总和 - 2*sum_A
return sum_total - 2 * sum_A;
}
};
复杂度分析:
- 时间复杂度:
O(1)
,仅涉及数学运算,与n
无关。 - 空间复杂度:
O(1)
,使用常数级变量。
四、解法对比
解法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
遍历法 | O(n) | O(1) | n 较小时有效 |
数学公式法 | O(1) | O(1) | 大规模数据最优 |
五、关键点总结
- 数学建模:将问题转化为等差数列求和,避免遍历,大幅提升效率。
- 边界处理:注意整数除法
n/m
会自动向下取整,正确计算能被m
整除的数的个数。 - 数据类型:使用
long
类型存储中间结果,避免整数溢出(例如当n
接近1e9
时,n*(n+1)
可能超过int
范围)。