数论:整除分块

本文介绍了一种方法,利用整除分块思想计算给定区间[X,Y]内每个数的约数和之和,通过求解Σ(y/i*i),并应用等差数列求和公式简化计算过程。给出C++代码示例实现。

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

Description

在下北泽大学读书的仙贝同学遇到了很麻烦的问题

对于X,函数f(X)表示X所有约数的和。

例如:f(8)=1+2+4+8=15。

对于一个X,仙贝很快算出f(X)。

仙贝不满足于此,给两个正整数X,Y(X<Y),仙贝希望尽快地算出f(X)+f(X+1)+……+f(Y)的值,你能帮助仙贝算出这个值吗?

他说答对了有奖励,答错了有惩罚,请你帮帮他。

Input

输入文件仅一行,两个正整数X和Y(X<Y),表示需要计算f(X)+f(X+1)+⋯+f(Y)。

Output

输出只有一行,为f(X)+f(X+1)+⋯+f(Y)的值。

Sample Input 1 

114 514

Sample Output 1

206961

Sample Input 2 

2 5

Sample Output 2

20

Hint

数据范围1≤X<Y≤2×10^9

首先可以通过前缀和思维得出 ans = f(y)-f(x-1)

然后根据经验发现 因数 i 在1到y 中出现 y/i 次,所以贡献为 y/i * i

也就是计算 因数 i 出现的次数 乘 i ,然后把所有1 - y 所有计算结果相加

然后计算Σ(y/i*i)可以使用整除分块。

整除分块思想就是快速得出Σ(y/i),模板如下,

ll f(ll n) {
	ll res = 0;
	int r = 0;
	for (ll l = 1; l <= n; l = r + 1) {
		r = min(n / (n / l ), n);
		res += (n / l) * (r - l + 1);
	}
	return res;
}

大概思路就是 n分别除以 1到 i 的所有结果中,相同结果总是挨在一起的,所以可以求相同结果的长度,然后直接求出 n/i 的所有相同结果的总和,所以直接遍历 1 到 i, l 为 n/i 相同结果区间的左端点,通过 n/(n/l) 求出右端点,然后就可以 (n / l) * (r - l + 1) 快速得出所有 n/i 相同的值。

回归原题,可以发现和整除分块思路相似, 变成了求 Σ(y/i*i),思路转换一下, n/i 表示的是 因素 i 在1-n 中出现的次数,也就是上文提到计算公式前半部分,然后 l-r 的区间中的值表示的是所有 n/i 的值都相等的 i 的值,也就是模板中 n/i 所需要乘的 值从 (r - l + 1) 表示的区间长度变成了区间中的所有值的和,然后区间中的值显然为等差数列,公差为1,最小值首项为 l,最大值末项为 r,

那么可以直接通过等差数列求和公式(首项+末项)*项数/2 快速求出值,也就是(r - l + 1) * (l + r) / 2

AC code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f(ll n) {
	ll res = 0;
	int r = 0;
	for (ll l = 1; l <= n; l = r + 1) {
		r = min(n / (n / l ), n);
		res += (n / l) * (r - l + 1) * (l + r) / 2;
	}
	return res;
}

int main() {
	int a, b;
	cin >> a >> b;
	ll res1 = f(a - 1), res2 = f(b);

//	long long res1 = 0, res2 = 0 ;
//	for (int i = 1; i  < a; i++) {
//		res1 += (a - 1) / i * i;
//	}
//	for (int i = 1; i  <= b; i++) {
//		res2 += b / i * i;
//	}
	cout << res2 - res1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值