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 514Sample Output 1
206961Sample Input 2
2 5Sample Output 2
20Hint
数据范围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; }