连续正整数的因子和之和问题
2022/8/23大约 3 分钟
题目大意
已知正整数
数据范围:
对于
对于
保证结果在long long
范围内
题目分析
题面非常的直白。令
接下来的问题就是,如何快速的求
显然我们有一个
先给出一个结论:
对于这个结论的证明(可以先跳过):
那么我们的问题就是如何快速的求出这个
显然我们不可能直接枚举求它,这就不算优化了。这个东西乍一看比较困难。我们尝试求一下
0, 0, 1, 0, 0, 4, 2, 4, 1, 0, 1, 4, 9, 2, 10, 4, 15, 10, 5, 0,
16, 12, 8, 4, 0, 22, 19, 16, 13, 10, 7, 4, 1,
32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0,
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25,
24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7,
6, 5, 4, 3, 2, 1, 0
我们可以发现:从某一项开始,他的值是由几段连续的等差数列拼成的,并且这几个等差数列的公差还是单调下降的一段连续自然数。这启发我们把它分解成几段连续的等差数列计算。
接下来就要考虑每段等差数列的起点问题。观察100的数据,我们发现,等差数列的起点分别是x/2+1
, x/3+1
, x/4+1
, x/5+1
等。那么我们只要枚举这个分母,然后利用等差数列求和即可。
关于这个等差数列的感性证明:
首先我们从大到小考虑。对于一个
但是我们仍然没有解决实际问题。枚举分母这个过程仍然是线性时间的。但是我们可以只枚举分母到平方根,求出
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
static ll x, y;
static ll ans;
static ll num, s, t, curr, res, ori;
ll calc(ll x) {
num = 1, curr = 1, res = 0, ori = x;
while (x * x > ori) {
s = ori % x;
t = ori % (ori / curr + 1);
num = x - ori / curr;
res += (s + t) * num / 2;
x = ori / curr;
++curr;
}
while (x) {
res += ori % x;
--x;
}
return ori * ori - res;
}
int main() {
cin >> x >> y;
ans = calc(y) - calc(x - 1);
cout << ans << endl;
return 0;
}