1.数论分块的含义
数论分块算法,就是枚举出使得取整函数发生变化的地方。
例如,对表达式 ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor ⌊in⌋使用数论分块算法,就可以在 O ( n ) O(\sqrt n) O(n)的时间复杂度下枚举所有满足 ⌊ n i − 1 ⌋ + 1 = ⌊ n i ⌋ \lfloor \frac{n}{i-1}\rfloor+1 = \lfloor \frac{n}{i} \rfloor ⌊i−1n⌋+1=⌊in⌋的 i 。
2.算法模板
long long r;
for(int l = 1; l <= n; l = r + 1)
{r = k/(k/l);/*your code*/
}
变量 l 就是取整函数发生变化的位置,即满足 ⌊ n l − 1 ⌋ + 1 = ⌊ n l ⌋ \lfloor \frac{n}{l-1}\rfloor+1 = \lfloor \frac{n}{l} \rfloor ⌊l−1n⌋+1=⌊ln⌋(l = 1除外)的位置,变量 r 就是满足 ⌊ n x ⌋ = ⌊ n l ⌋ \lfloor \frac{n}{x}\rfloor = \lfloor \frac{n}{l} \rfloor ⌊xn⌋=⌊ln⌋的所有x 中最大的一个。
直观上,该过程时间复杂度小于 O ( n ) O(n) O(n),因为每次往后跳的长度大于1,
该算法的实际复杂度为 O ( n ) O(\sqrt n) O(n)。
正确性证明和时间复杂度证明,详见此处。写题不需要证明。
3.算法的优化点
将所有 ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor ⌊in⌋结果一样的 i 一并取出,使得时间复杂度降为 O ( n ) O(\sqrt n) O(n)。
涉及取整的地方均可能用到此算法,包括但不限于,整除(练习题1)、最大公因数(练习题3)、最小公倍数(练习题2)、取模(本文例题)。
4.例题
P2261 [CQOI2007] 余数求和
题目描述
给出正整数 n n n 和 k k k,请计算
G ( n , k ) = ∑ i = 1 n k m o d i G(n, k) = \sum_{i = 1}^n k \bmod i G(n,k)=i=1∑nkmodi
其中 k m o d i k\bmod i kmodi 表示 k k k 除以 i i i 的余数。
输入格式
输入只有一行两个整数,分别表示 n n n 和 k k k。
输出格式
输出一行一个整数表示答案。
输入输出样例 #1
输入 #1
10 5
输出 #1
29
说明/提示
样例 1 解释
G ( 10 , 5 ) = 0 + 1 + 2 + 1 + 0 + 5 + 5 + 5 + 5 + 5 = 29 G(10, 5)=0+1+2+1+0+5+5+5+5+5=29 G(10,5)=0+1+2+1+0+5+5+5+5+5=29。
数据规模与约定
- 对于 30 % 30\% 30% 的数据,保证 n , k ≤ 1 0 3 n , k \leq 10^3 n,k≤103。
- 对于 60 % 60\% 60% 的数据,保证 n , k ≤ 1 0 6 n, k \leq 10^6 n,k≤106。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n , k ≤ 1 0 9 1 \leq n, k \leq 10^9 1≤n,k≤109。
解题思路
- a % b = a − ⌊ a b ⌋ ∗ b a \% b =a- \left \lfloor \frac{a}{b} \right \rfloor *b a%b=a−⌊ba⌋∗b
- 求和时,前半部分和后半部分,分开处理。
- 数列 a i = i ∗ ⌊ x i ⌋ a_i =i * \left \lfloor \frac{x}{i} \right \rfloor ai=i∗⌊ix⌋,在 ⌊ x i ⌋ \left \lfloor \frac{x}{i} \right \rfloor ⌊ix⌋为不变值的时候,是等差数列。
AC代码
#include<bits/stdc++.h>
#define int long longusing namespace std;
typedef pair<int,int> PII;
const int mod = 998244353;
void solve()
{int n,k;cin >> n >> k;long long ans = 0;ans += n*k;int r;for(int i = 1; i <= n; i = r + 1){if(k/i == 0) break;r = min(k/(k/i),n);ans -= (r-i+1)*(i+r)/2 *(k/i);}cout << ans << '\n';
}signed main()
{ios::sync_with_stdio(false);cin.tie(0);int t = 1;//cin >> t;while(t --){solve();}
}
练习题
- 【AtCoder Regular Contest 150B】
- 【2025CCPC北京市赛】
- 【2021ICPC陕西省赛】