【数论分块】数论分块算法模板及真题

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.算法模板

cpp 复制代码
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代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long

using 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();
    }
}

练习题

相关推荐
金融小师妹35 分钟前
应用BERT-GCN跨模态情绪分析:贸易缓和与金价波动的AI归因
大数据·人工智能·算法
广州智造41 分钟前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
Trent19853 小时前
影楼精修-肤色统一算法解析
图像处理·人工智能·算法·计算机视觉
feifeigo1233 小时前
高光谱遥感图像处理之数据分类的fcm算法
图像处理·算法·分类
北上ing4 小时前
算法练习:19.JZ29 顺时针打印矩阵
算法·leetcode·矩阵
.格子衫.5 小时前
真题卷001——算法备赛
算法
XiaoyaoCarter5 小时前
每日一道leetcode
c++·算法·leetcode·职场和发展·二分查找·深度优先·前缀树
Hygge-star5 小时前
【数据结构】二分查找5.12
java·数据结构·程序人生·算法·学习方法
June`7 小时前
专题二:二叉树的深度搜索(二叉树剪枝)
c++·算法·深度优先·剪枝
加什么瓦7 小时前
Redis——底层数据结构
数据结构