CQOI2007 余数之和

本文介绍了一种计算j(n,k)=kmod1+kmod2+...+kmodn值的有效方法,通过数学变换简化计算过程,并采用分段统计的方式降低时间复杂度至O(√k)。

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

Time Limit: 5 Sec Memory Limit: 128 MB

Description

给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值
其中k mod i表示k除以i的余数。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7

Input

输入仅一行,包含两个整数n, k。
1<=n ,k<=10^9

Output

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

Sample Input

5 3

Sample Output

7

简要题解

我们可以把原式数学化一下:求\(\sum \limits_{i=1}^n k \mod i\)
我们可以发现
\[ \begin{align*} \text{原式}&=\sum \limits_{i=1}^n k \mod i\\ &=\sum \limits_{i=1}^n k- \lfloor \frac ki \rfloor \cdot i\\ &=nk - \sum \limits_{i=1}^n \lfloor \frac ki \rfloor \cdot i \end{align*} \]
显然,只要\(\lfloor \frac ki \rfloor\)的值在一段段i的范围内是一样的。我们的任务就是要求出每一段这样的范围。我们令\(f(x)=\lfloor \frac kx \rfloor \qquad g(x)=\lfloor \frac k{\lfloor \frac kx \rfloor} \rfloor\),那么其实直觉就可以告诉我们\(g(x)\)就可以表示f值=\(\lfloor \frac kx \rfloor\)的最大的数。然而数学毕竟是一门严谨的科学,我们可能需要来证明一下。
显然\(\lfloor \frac kx \rfloor \leq \frac kx\),那么\(g(x)=\lfloor \frac k{\lfloor \frac kx \rfloor} \rfloor \geq \lfloor \frac k{\frac kx } \rfloor = x \text{即} g(x) \geq x\)。所以有\(\lfloor \frac k{g(x)} \rfloor \leq \lfloor \frac kx \rfloor\)
同时,\(\lfloor \frac k{g(x)} \rfloor = \lfloor \frac k{\lfloor \frac k{\lfloor \frac kx \rfloor} \rfloor} \rfloor \geq \lfloor \frac k{ \frac k{\lfloor \frac kx \rfloor} } \rfloor = \lfloor \frac kx \rfloor\),即\(\lfloor \frac k{g(x)} \rfloor \geq \lfloor \frac kx \rfloor\)\(\lfloor \frac k{g(x)} \rfloor \leq \lfloor \frac kx \rfloor\),所以\(\lfloor \frac k{g(x)} \rfloor = \lfloor \frac kx \rfloor\)
所以\(\forall i \in [x,\lfloor \frac k{\lfloor \frac kx \rfloor} \rfloor]\)\(\lfloor \frac ki \rfloor\)的值都相等!其实之前的猜想的“最大”是很显然的,也没必要再去证一遍了,就算不是最大的,也不影响我们的这个程序。
下面我们就有了一个算法:统计\([1,g(1)]\)的区间里的\(\lfloor \frac ki \rfloor \cdot i\)的和,既然\(\lfloor \frac ki \rfloor\)都一样,那就用等差数列求和公式来算一下即可。然后在从\(g(1)+1\)\(g(g(1)+1)\)这段区间再如此统计……重复上述步骤,直到\(i>k\),此时\(\lfloor \frac ki \rfloor\)一定等于0,直接令g(i)=n,统计i..n即可。
下面我们算一下时间复杂度。这个时间复杂度,应该等于\(\lfloor \frac ki \rfloor\)有多少个不同的取值是一样的。当\(i \leq \sqrt k\)时,i只有\(\sqrt k\)中取值,所以\(\lfloor \frac ki \rfloor\)也最多只有\(\sqrt k\)种取值。当\(i > \sqrt k\)时,\(\lfloor \frac ki \rfloor < \sqrt k\),所以\(\lfloor \frac ki \rfloor\)也最多只有\(\sqrt k\)种取值,所以\(\lfloor \frac ki \rfloor\)一共最多\(2 \sqrt k\)种取值。所以该算法的之间复杂度为\(O(\sqrt k)\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;

int n,k;ll ans;

int main(){
    scanf("%d%d",&n,&k);ans=1ll*n*k;
    for(register int i=1,g;i<=n;i=g+1){
        if(k/i!=0)g=min(n,k/(k/i));else g=n;//错误笔记:如果k/i==0即k<i的话,k/(k/i)会炸掉,所以要特判一下。 
        ans-=(ll)(k/i)*(i+g)*(g-i+1)/2;
    }
    printf("%lld\n",ans);
}

转载于:https://www.cnblogs.com/hankeke/p/CQOI2007-sum.html

### Matlab 中计算除法的余数商 在 MATLAB 中可以利用 `mod()` `rem()` 函数来获取两个数相除后的余数。对于商而言,可以通过简单的除法操作或是结合取整函数得到。 当处理正数时,`b = mod(a,m)` 返回的是 a 除以 m 的非负余数[^1]。而 `r = rem(a,b)` 则返回 a 除以 b 后的余数,其符号被除数相同。需要注意的是,在某些情况下两者的结果可能不同,这取决于输入的具体情况以及它们之间的关系[^2]。 为了获得两数相除所得之商,可以直接执行除法运算并应用不同的取整方式: - 使用 `fix(a/b)` 可取得向零方向舍入的商; - 若希望向下取整则采用 `floor(a/b)`; - 对于向上取整的情况适用 `ceil(a/b)`; - 若要四舍五入到最接近的整数,则可选用 `round(a/b)`。 下面给出一段示例代码用于展示如何同时求得给定一对数字间的商及其对应的余数: ```matlab % 定义变量 dividend = 26; divisor = 7; % 计算商 quotient_fix = fix(dividend / divisor); quotient_floor = floor(dividend / divisor); quotient_ceil = ceil(dividend / divisor); quotient_round = round(dividend / divisor); % 计算余数 remainder_mod = mod(dividend, divisor); remainder_rem = rem(dividend, divisor); disp([&#39;Quotient (towards zero): &#39;, num2str(quotient_fix)]); disp([&#39;Quotient (downward): &#39;, num2str(quotient_floor)]); disp([&#39;Quotient (upward): &#39;, num2str(quotient_ceil)]); disp([&#39;Quotient (rounded): &#39;, num2str(quotient_round)]); disp([&#39;Remainder using mod(): &#39;, num2str(remainder_mod)]); disp([&#39;Remainder using rem(): &#39;, num2str(remainder_rem)]); ``` 上述代码片段展示了多种可能性下的商两种方法得出的不同形式的余数。通过调整 dividend divisor 的,可以根据实际需求测试其他例子。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值