数论分块

Posted wifepi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数论分块相关的知识,希望对你有一定的参考价值。

数论分块大致用于处理形如求Σ(1,n)  (k div i) 的问题

打表易得,(k div i)的值是线性的,因为向下取整,所以会出现值成段的现象,这样我们原先暴力的O(n)的算法可以得到优化

首先我们要知道一个定理

对于(k div i)而言最多有2√k个取值

 

证明:对于 i (1 <= i <= n, 且 i 是整数)而言,i 可以分成两种情况

  1. i <= √k , i 最多有 √k 个取值
  2. i >= √k , 那么 (k div i)<= √k,最多有√k个取值

所以我们可以拥有一个O(√k)的复杂度


 

对于怎么确定一个块值得左右边界,这里直接给出结论

对于一个块而言,假设左边界为LT,右边界为RT。那么有等式 RT = K / (K / LT)

 


 

所以我们就可以形如下式进行操作

 

 for(ll l = 1, r = 0 ; l <= n ; l = r + 1)
    {
        if(k / l) r = min(n, k/(k/l)) ;
        else r = n ;
        do something
    }

 

为了显得这篇文章不那么划水,所以还是要带一题例题

BZOJ 1257

题意就是让我们求 Σ(1,n) (k mod i)

那么怎么转换到今天的知识呢?

我们都知道 k mod i = k - (k div i) * i 

原式 = n* k - Σ(1,n) (k div i) * i 

对于一个值块(l , r)而言 (k div i) 的值是确定的,我们可以把它看成常数,这里假设成T,那么对于值块(l , r) 后一块可转换成 (k div i) * Σ(l,r) i 等差数列求和

这题就搞定了

技术图片
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, k, ans = 0 ;
int main(int argc, char const *argv[])
{
    scanf("%lld %lld",&n,&k) ;
    ans = k * n ;
    for(ll l = 1, r = 0 ; l <= n ; l = r + 1)
    {
        if(k / l) r = min(n, k/(k/l)) ;
        else r = n ;
        ans -= (k / l) * (r - l + 1) * (l + r) / 2ll ;
    }
    printf("%lld
",ans) ;
    return 0;
}
View Code

 

以上是关于数论分块的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3309 DZY Loves Math —— 莫比乌斯反演+数论分块

数论分块之整除分块

Luogu5307 [COCI2019] Mobitel 数论分块递推

《夜深人静写算法》数论篇 - (23) 整数分块

AcWing - 199 - 余数之和 = 数论分块

数论分块