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

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

练习题

相关推荐
hn小菜鸡5 小时前
LeetCode 377.组合总和IV
数据结构·算法·leetcode
Deepoch6 小时前
Deepoc 大模型:无人机行业的智能变革引擎
人工智能·科技·算法·ai·动态规划·无人机
heimeiyingwang9 天前
【深度学习加速探秘】Winograd 卷积算法:让计算效率 “飞” 起来
人工智能·深度学习·算法
时空自由民.9 天前
C++ 不同线程之间传值
开发语言·c++·算法
ai小鬼头9 天前
AIStarter开发者熊哥分享|低成本部署AI项目的实战经验
后端·算法·架构
小白菜3336669 天前
DAY 37 早停策略和模型权重的保存
人工智能·深度学习·算法
zeroporn9 天前
以玄幻小说方式打开深度学习词嵌入算法!! 使用Skip-gram来完成 Word2Vec 词嵌入(Embedding)
人工智能·深度学习·算法·自然语言处理·embedding·word2vec·skip-gram
亮亮爱刷题9 天前
飞往大厂梦之算法提升-7
数据结构·算法·leetcode·动态规划
_周游9 天前
【数据结构】_二叉树OJ第二弹(返回数组的遍历专题)
数据结构·算法
双叶8369 天前
(C语言)Map数组的实现(数据结构)(链表)(指针)
c语言·数据结构·c++·算法·链表·哈希算法