两数和的差题解

题目链接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)大规模数据最优

五、关键点总结

  1. 数学建模:将问题转化为等差数列求和,避免遍历,大幅提升效率。
  2. 边界处理:注意整数除法 n/m 会自动向下取整,正确计算能被 m 整除的数的个数。
  3. 数据类型:使用 long 类型存储中间结果,避免整数溢出(例如当 n 接近 1e9 时,n*(n+1) 可能超过 int 范围)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.wei-upup

如若对您有用,盼您赏个鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值