题目背景
Smart最近沉迷于对约数的研究中。
题目描述
对于一个数X,函数f(X)表示X所有约数的和。例如:f(6)=1+2+3+6=12。对于一个X,Smart可以很快的算出f(X)。现在的问题是,给定两个正整数X,Y(X<Y),Smart希望尽快地算出f(X)+f(X+1)+……+f(Y)的值,你能帮助Smart算出这个值吗?
输入格式:
输入文件仅一行,两个正整数X和Y(X<Y),表示需要计算f(X)+f(X+1)+……+f(Y)。
输出格式:
输出只有一行,为f(X)+f(X+1)+……+f(Y)的值。
思路
首先利用前缀和的思想, 我们只要能求出 1−n 的约数和的和, 就能求出一段区间的约数和的和
那么如何快速求出从 1 开始约数和的和呢
很朴素大暴力万岁!的算法是
o
n
o\sqrt{n}
on计算每个数的约数进行累加 ,然后慢到爆炸
我们可以换种思维, 考虑枚举约数
显然对于一个约数 d , 在 1−n 中出现过
⌊
n
d
⌋
\left\lfloor\frac{n}{d}\right\rfloor
⌊dn⌋ 次, 所以这一约数贡献的答案为
⌊
n
d
⌋
∗
d
\left\lfloor\frac{n}{d}\right\rfloor * d
⌊dn⌋∗d
所以 1−n 总因数和为
∑
i
=
1
n
⌊
n
i
⌋
∗
i
\sum_{i = 1}^{n}\left\lfloor\frac{n}{i}\right\rfloor * i
∑i=1n⌊in⌋∗i
这样依然需要枚举 1−n 这 n 个因数, O(n) 依然达不到复杂度要求
其实看到 ⌊ n d ⌋ \left\lfloor\frac{n}{d}\right\rfloor ⌊dn⌋ 时我们很兴奋: 除法分块!
(之前学的除法分块是假的。。现在补个真的)
比如说当 n=12 时, 我们分别计算
⌊
n
i
⌋
\left\lfloor\frac{n}{i}\right\rfloor
⌊in⌋ (1<=i<=n) 可以得到以下结果:12,6,4,3,2,2,1,1,1,1,1,1
注意有部分连续的向下取整值是一样的!除法分块就是 利用打表找规律 来求出每个区间的左右端点以达到进行快速运算的目的
左端点很容易求解, 即为上一个右端点 +1(初始值为1)
右端点通过打表找规律可知: r=n/(n/l)
意思是因子为1的有12个,贡献为12 * 1 = 12,因子为2的有6个,贡献为6 * 2 = 12,因子为3的有4个,贡献为3 * 4 = 12,以此类推。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x, y;
ll get_sum(ll n)
{
ll ans = 0;
/// l~r之间的所有数都是相同的因子,且每个因子的个数都为n/l
/// 即为 l*(n/l) + (l+1)*(n/l) + .... r*(n/l); (n/l)提取出来
for(ll l = 1,r; l <= n; l = r + 1)
{
r = n / (n / l);
ans += (n / l) * (l + r) * (r - l + 1) / 2;
cout << l << ' ' << r << ' ' << (n / l) * (l + r) * (r - l + 1) / 2 << endl;
system("pause");
}
return ans;
}
int main()
{
cin >> x >> y;
cout << get_sum(y) - get_sum(x-1) << endl;
return 0;
}