【数论】数论分块

2024-18-19 ·最后更新时间:2024-18-19

1 , s o l u t i o n ( 1 ) \Large\mathcal{1,solution(1)} 1,solution(1)
如果我们把数 n n n 与小于等于 n n n 的数 i i i 的对应关系打印在表格上会是这样。

i i i12345678910
⌊ n i ⌋ \Large\lfloor\frac{n}{i}\rfloor in10532211111

可以发现 ⌊ n i ⌋ \Large\lfloor\frac{n}{i}\rfloor in 的数是有重复的,那么我们可不可以根据重复的特点来计算 ⌊ n i ⌋ \Large\lfloor\frac{n}{i}\rfloor in 的数值呢?答案是可以的,这也就是数论分块的主要思想。

2 , v e r i f i c a t i o n \Large\mathcal{2,verification} 2,verification
为了保证算法的正确性我们要对其进行时间复杂度分析。

  1. i > n i>\sqrt{n} i>n 时,那么此时 ⌊ n i ⌋ \Large\lfloor\frac{n}{i}\rfloor in 最多有 n \sqrt{n} n 种取值。
  2. i ≤ n i\leq\sqrt{n} in 时,那么此时 ⌊ n i ⌋ \Large\lfloor\frac{n}{i}\rfloor in 也最多有 n \sqrt{n} n 种取值。

所以最终我们可以得到时间复杂度为 O ( n ) O(\sqrt{n}) O(n )

3 , s o l u t i o n ( 2 ) \Large\mathcal{3,solution(2)} 3,solution(2)
接下来我们将要对边界进行分析,判断什么时候当前在哪一个块中。

首先我们假设当前块的左端点是 l l l ,那么根据我们之前的分析 ⌊ n l ⌋ \lfloor\frac{n}{l}\rfloor ln 就一定是我们当前块的数值,那么右端点该怎么求,对右端点 r r r 进行分析,其本质上就是满足 ⌊ n l ⌋ = ⌊ n r ⌋ \lfloor\frac{n}{l}\rfloor=\lfloor\frac{n}{r}\rfloor ln=rn r r r 的最大值,等等,那不就是说明了 r = ⌊ n ⌊ n l ⌋ ⌋ r={\Large\lfloor}\frac{n}{\lfloor\frac{n}{l}\rfloor}\Large\rfloor r=lnn 吗!?

那么这样我们就顺利的得到了 r r r 的值并且可以求出当前块的所有数的和,也就是 S = ( r − l + 1 ) × ⌊ n l ⌋ S=(r-l+1)×\lfloor\frac{n}{l}\rfloor S=(rl+1)×ln

4 , c o d e \Large\mathcal{4,code} 4,code

int division_block(int n){
    int ans=0;
    for(int l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans+=n/l*(r - l + 1);
    }
    return ans;
}

5 , e x a m p l e \Large\mathcal{5,example} 5,example
题目连接:[CQOI2007] 余数求和
思路:因为 a m o d    b a \mod b amodb 可以转换为 a − ⌊ a b ⌋ b a-\lfloor\frac{a}{b}\rfloor b abab 所以原式也就转化成了 ∑ i = 1 n k − ⌊ k i ⌋ i {\Large\sum_{i=1}^n} k-\lfloor\frac{k}{i}\rfloor i i=1nkiki 可以发现 k k k 不会随着 i i i 的变化而变化,所以可以将 k k k 放在西格玛外面, n k − ∑ i = 1 n ⌊ k i ⌋ i nk-{\Large\sum_{i=1}^n}\lfloor\frac{k}{i}\rfloor i nki=1niki 于是在这么多奇妙转化下,我们可以发现式子最后不就变成了数论分块的模板吗!?所以直接套模板就行,细节看代码。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,ans;
signed main(){
	cin>>n>>k;
	for(int l=1,r;l<=n;l=r+1){
		r=k/l==0?n:min(n,k/(k/l));
		ans+=(k/l)*(l+r)*(r-l+1)/2; 
	}
	cout<<n*k-ans<<endl;
	return 0;
}

代码有惊喜 q w q \color{white}{代码有惊喜qwq} 代码有惊喜qwq

安利一下我的博客qwq

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值