余数之和(数论、数学题)

博客探讨了数论问题j(n, k),即求k除以1到n的余数之和,并给出了算法设计和优化思路,利用单调递减性质减少计算复杂度,确保在大数据范围内有效计算。" 5253782,777586,软通动力参观武汉众嵌科技:深化嵌入式人才培养合作,"['嵌入式', '教育', '人力资源', '外包服务', 'IT行业']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值。

例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7。

输入格式

输入仅一行,包含两个整数n, k。

输出格式

输出仅一行,即j(n, k)。

数据范围

1≤n,k≤1e9

输入样例

5 3

输出样例

7

思路:

这题,纯数学题。
已知 k   m o d   i = k − ⌊ k / i ⌋ ∗ i k \ mod\ i = k - \lfloor k / i \rfloor * i k mod i=kk/ii
所以: ∑ i = 1 n k   m o d   i = n ∗ k − ∑ i = 1 n ⌊ k / i ⌋ ∗ i \sum_{i=1}^n{k\ mod\ i} = n * k - \sum_{i=1}^n{\lfloor k / i \rfloor * i} i=1nk mod i=nki=1nk/ii
由此可以看出,我们必须将 ∑ i = 1 n ⌊ k / i ⌋ ∗ i {\sum_{i=1}^n\lfloor k / i \rfloor * i} i=1nk/ii 进行简化计算。
⌊ k / i ⌋ \lfloor k / i \rfloor k/i 随着 i i i 的递增,是一个递减的趋势,也就是一个单调递减的性质。
对于任意的整数 l ∈ [ 1 , k ] l\in[1, k] l[1,k],设 r = ⌊ k / ⌊ k / l ⌋ ⌋ r=\lfloor k/\lfloor k/l\rfloor\rfloor r=k/k/l
⌊ k / l ⌋ ≤ ( k / l ) \lfloor k / l\rfloor\leq(k/l) k/l(k/l)
显然有 r ≥ ⌊ k / ( k / l ) ⌋ = x r\geq\lfloor k/(k/l)\rfloor=x rk/(k/l)=x,所以, ⌊ k / r ⌋ ≤ ⌊ k / l ⌋ \lfloor k/r\rfloor\leq\lfloor k/l\rfloor k/rk/l
又因为, ⌊ k / r ⌋ ≥ ⌊ k / ( k / ⌊ k / l ⌋ ) ⌋ = ⌊ k / k ∗ ⌊ k / l ⌋ ⌋ = ⌊ k / l ⌋ \lfloor k / r\rfloor\geq\lfloor k/(k/\lfloor k/l\rfloor)\rfloor=\lfloor k/k*\lfloor k/l\rfloor\rfloor=\lfloor k/l\rfloor k/rk/(k/k/l)=k/kk/l=k/l
所以: ⌊ k / r ⌋ = ⌊ k / l ⌋ \lfloor k / r\rfloor=\lfloor k/l\rfloor k/r=k/l
并且对于任意的 i ∈ [ l , ⌊ k / ⌊ k / l ⌋ ⌋ ] i\in[l, \lfloor k/\lfloor k/l\rfloor\rfloor] i[l,k/k/l] ⌊ k / i ⌋ \lfloor k/i\rfloor k/i的值都相等,图像为一段平行于x轴的线段。这样,我们就可以将循环的范围进行缩小,优化了效率。

接下来证明一下循环的范围。

∀ i ∈ [ 1 , k ] \forall i\in[1,k] i[1,k]

  1. i ≤ k i\leq\sqrt k ik 时, i i i 1 ⇒ k 1\Rightarrow \sqrt k 1k 的整数,所以 ⌊ k / i ⌋ \lfloor k/i\rfloor k/i最多只有 k \sqrt k k 个不同的值。
  2. i > k i>\sqrt k i>k 时, ⌊ k / i ⌋ < k \lfloor k/i\rfloor<\sqrt k k/i<k ,又因为 ⌊ k / i ⌋ \lfloor k/i\rfloor k/i是向下取整,故 ⌊ k / i ⌋ \lfloor k/i\rfloor k/i也最多只有 k \sqrt k k 个不同的取值。

所以, ⌊ k / i ⌋ \lfloor k/i\rfloor k/i至多只有 2 k 2\sqrt k 2k 个不同的值。
综上所述,对于 i = [ 1 , k ] i=[1,k] i=[1,k] ⌊ k / i ⌋ \lfloor k/i\rfloor k/i由不超过 2 k 2\sqrt k 2k 不同的值段组成,每一段的值都相等。

所以对于每一段 ∑ i = l r ⌊ k / i ⌋ ∗ i {\sum_{i=l}^r\lfloor k / i \rfloor * i} i=lrk/ii 可以简化成 ⌊ k / l ⌋ ∗ ∑ i = l r i \lfloor k / l\rfloor*\sum_{i=l}^ri k/li=lri
∑ i = l r i \sum_{i=l}^ri i=lri 是一个等差数列求和,代代求和公式就可求出。

算法设计:
  1. 先让 l = 1 l=1 l=1,再计算出求和的上界 r r r,最后代公式计算整段的值
  2. 更新每段的段首,即 l = r + 1 l=r+1 l=r+1
  3. 重复1、2步,直到取完所有的可选段,即当 k / l k/l k/l 为零的时候,上界就取完了。

注意数据范围,可能会炸int,所以索性全开long long 了。
注意一下上界只能取到右端点n,如果大于n,这段值为零是没有贡献的,所以得判断一下 ⌊ k / ⌊ k / l ⌋ ⌋ \lfloor k /\lfloor k / l\rfloor\rfloor k/k/l n n n 的大小关系,上界取min

代码:
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

LL n, k, res;

int main()
{
    cin >> n >> k;
    res = n * k;
    for(int l = 1, r; l <= n; l = r + 1)
    {
        //判断上界是否为0,如果为0,则说明取到无值贡献的段了,可以直接break掉,也可以让r等于右端点n。
        r = k / l ? min(k/(k/l), n) : n;
        //(l + r) * (r - l + 1) / 2 是等差数列求和
        res -= (k/l) * (l + r) * (r - l + 1) / 2;
    }
    cout << res << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值