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

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

练习题

相关推荐
艾莉丝努力练剑33 分钟前
【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
c语言·开发语言·数据结构·c++·学习·算法
闪电麦坤952 小时前
数据结构:迭代方法(Iteration)实现树的遍历
数据结构·二叉树·
C++、Java和Python的菜鸟2 小时前
第六章 统计初步
算法·机器学习·概率论
Cx330❀2 小时前
【数据结构初阶】--排序(五):计数排序,排序算法复杂度对比和稳定性分析
c语言·数据结构·经验分享·笔记·算法·排序算法
散1122 小时前
01数据结构-Prim算法
数据结构·算法·图论
起个昵称吧2 小时前
线程相关编程、线程间通信、互斥锁
linux·算法
..过云雨3 小时前
01.【数据结构-C语言】数据结构概念&算法效率(时间复杂度和空间复杂度)
c语言·数据结构·笔记·学习
myzzb3 小时前
基于uiautomation的自动化流程RPA开源开发演示
运维·python·学习·算法·自动化·rpa
拂晓银砾3 小时前
Java数据结构-栈
java·数据结构
旺小仔.4 小时前
双指针和codetop复习
数据结构·c++·算法