数论分块
数论分块
数论分块可以快速计算一些含有除法向下取整的和式 (即形如
它主要利用了富比尼定理 (
引入
题目转换一下就是求
朴素做法,遍历
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int ans = 0;
for (int i = 1; i <= n; ++i) {
ans += n / i;
}
System.out.println(ans);
}
}
不能解决
当
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
21 | 10 | 7 | 5 | 4 | 3 | 3 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
观察发现,
如果我们知道了每一块的值和长度(左右边界),也就可以使用乘法运算来代替加法运算了
求某一值所在块的右端点
假设求
因此,
实现
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
long ans = 0;
for (long l = 1, r; l <= n; l = r + 1) {
//取min防止越界
r = Math.min(n / (n / l),n);
ans += n / l * (r - l + 1);
}
System.out.println(ans);
}
}
时间复杂度分析
证明:
当
时, 有 种取值。
当时, 至多有 种取值。 综上,分块的块数
因此,时间复杂度为
例题
约数和
题意概述
原题链接:P2424 约数和 - 洛谷
给定正整数
解题思路
求
因此,该问题变成了求出
与洛谷 P1403 约数研究类似,求每个数的约数和,可以转换成求
因此,式子可以转化为
因为乘法满足分配律,对于
Code
import java.util.Scanner;
public class Main {
//计算 1~n 所有约数的和
static long calculate(long n) {
long ans = 0;
for (long l = 1, r; l <= n; l = r + 1) {
r = Math.min(n / (n / l), n);
ans += (l + r) * (r - l + 1) / 2 * (n / l);
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int x = sc.nextInt(), y = sc.nextInt();
System.out.println(calculate(y) - calculate(x - 1));
}
}
余数求和
题意概述
原题链接:P2261 余数求和 - 洛谷
给定正整数
解题思路
和式部分与上一题仅为分子变了
Code
注意点:
- 数据范围,两数相乘可能超出
范围 - 当
时,不能再求右端点,会除零错误
import java.util.Scanner;
public class Main {
//计算 1~n 所有约数的和
static long calculate(long n, long k) {
long ans = 0;
//当左端点小于等于k时,才有右端点
for (long l = 1, r; l <= n && l <= k; l = r + 1) {
r = Math.min(k / (k / l), n);
ans += (l + r) * (r - l + 1) / 2 * (k / l);
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), k = sc.nextInt();
//两数相乘可能超出int
System.out.println((long) n * k - calculate(n, k));
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人