题目链接
题目描述
对于给定的正整数 ,求表达式
的值。
解题思路
本题是 整除分块(也称 数论分块)算法的又一个典型应用。
由于 的范围可以达到
,使用
的朴素循环来计算每一项的和是不可行的,会导致超时。
核心观察与分块计算
我们注意到,在表达式 中,存在许多连续的
会得到完全相同的商。我们可以将这些
划分成块,并对每个块进行整体计算。
假设对于一个块,其范围是 [l, r]
,在这个区间内所有的 都有相同的商
。那么这个块对总和的贡献为:
其中 是一个等差数列,其和为
。
块的边界与算法流程
算法的关键在于,对于一个块的左边界 ,我们可以通过公式
快速计算出它的右边界
。
算法流程如下:
-
初始化答案
ans = 0
,左边界l = 1
。 -
当
l <= N
时,循环:a. 计算当前块的商
v = N / l
和右边界r = N / v
。b. 计算等差数列的和
sum_i = (l + r) * (r - l + 1) / 2
。c. 将贡献
v * sum_i
累加到ans
中。d. 更新
l = r + 1
,进入下一个块。
该算法的时间复杂度为 。
关于数据溢出
当 达到
时,最终答案和中间计算结果(如
v * sum_i
)可能会超过标准的64位整数(C++ 中的 long long
)的表示范围(约 )。因此,需要使用能够处理更大数值的整数类型,例如 C++ 中的
__int128_t
、Java 中的 BigInteger
,或者利用 Python 对大整数的内置支持。
代码
#include <iostream>
using namespace std;
// 用于输出 __int128_t 类型的函数
void print_128(__int128_t n) {
if (n == 0) {
cout << 0;
return;
}
string s = "";
while (n > 0) {
s += (n % 10) + '0';
n /= 10;
}
for (int i = s.length() - 1; i >= 0; --i) {
cout << s[i];
}
}
int main() {
long long n;
cin >> n;
__int128_t ans = 0;
for (long long l = 1, r; l <= n; l = r + 1) {
long long v = n / l;
r = n / v;
__int128_t sum_l = l;
__int128_t sum_r = r;
// 计算 (l+r)*(r-l+1)/2
__int128_t sum_i = (sum_l + sum_r) * (sum_r - sum_l + 1) / 2;
ans += v * sum_i;
}
print_128(ans);
cout << "\n";
return 0;
}
import java.util.Scanner;
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
BigInteger ans = BigInteger.ZERO;
BigInteger two = BigInteger.valueOf(2);
for (long l = 1, r; l <= n; l = r + 1) {
long v = n / l;
r = n / v;
BigInteger bigL = BigInteger.valueOf(l);
BigInteger bigR = BigInteger.valueOf(r);
BigInteger bigV = BigInteger.valueOf(v);
// (l+r) * (r-l+1) / 2
BigInteger sumI = bigL.add(bigR)
.multiply(bigR.subtract(bigL).add(BigInteger.ONE))
.divide(two);
ans = ans.add(bigV.multiply(sumI));
}
System.out.println(ans);
}
}
def solve():
n = int(input())
ans = 0
l = 1
while l <= n:
v = n // l
r = n // v
# 等差数列求和 (l+r)*(r-l+1)//2
sum_i = (l + r) * (r - l + 1) // 2
ans += v * sum_i
l = r + 1
print(ans)
solve()
算法及复杂度
- 算法:整除分块 (Integer Division Block)
- 时间复杂度:
。
- 空间复杂度:
。