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^9Output
输出仅一行,即j(n, k)。
Sample Input
5 3
Sample Output
7
思路描述:
取模运算没有啥比较好的运算公式,故一般转化为除法运算。
f(n, k) = k mod 1 + k mod 2 + ... + k mod n
= k-floor(k/1)*1 + k-floor(k/2)*2 + ... + k-floor(k/n)*n.
= n * k - (floor(k/1)*1 + floor(k/2)*2 + ... + floor(k/n)*n).
1.因为对于计算式 k / x,最多有 2 * sqrt(k) 个不同的数,所以上述括号中floor部分最多有 十万 左右个不同的结果.
证明如下: 当 x <= sqrt(k)时,显然有不超过sqrt(K)个结果,当x > k时,显然 1 < x / k < sqrt(k),所以结论成立。
2.所以括号部分最多被分为1e5个段,每个段中具有相同的结果。所以只要知道了每一段的首项和末项,就可以利用等差公式在十万级别内算出最终结果.
3.每一段的末项可由如下结论得到:和 floor(k/x) 相等的最大 x 等于 floor(k/floor(k/x)). 所以代码可实现.
证明过程暂略。
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main () {
int n, k;
LL sum;
while (cin >> n >> k) {
if (n > k) sum = (LL) (n - k) * k, n = k;
sum += (LL) n * k;
int p = 1;
while (p <= n) {
int l = p, r = k / (k / p);
sum -= (LL) k / p * (l + r) * (r - l + 1) / 2;
p = r + 1;
}
cout << sum << endl;
}
return 0;
}
THE END;