[数学基础] 10 数论分块

数论分块

简介

数论分块通常被用来以O(n)的复杂度快速计算形如i=1nf(i)g(ni)的含有除法向下取整的和式,它的核心思想是将ni相同的数打包同时计算,主要利用了Fubini定理。

证明

1. 证明时间复杂度为O(n)

引理1 对于任意一个正整数nnd(d[1,n])的数量级为n

  • dn,假设所有的nd取值均不同,则存在n种结果
  • d>n,则nd<n,取值同样有n

因此,所有的可能取值一定2n,即整数分块的复杂度为O(n)

2. 证明算法的正确性

引理2 对于i, 若满足ni=C,则所有满足条件的i一定是一段连续区间的集合。

反证法,若不是连续区间,则一定t(l,r),使得nl=nrnlnt

t>l,t<r

ntnl,ntnr ,即nt=nl,矛盾,故得证。

**引理3 **若满足ni=Ci集合为i[l,r],则只需要知道l,就可以求出对应的C,r

显然,C=nl,下面就要证明r=nnl=nC

p=nC,则n=p×C+k,k[0,min{p,C}),下面要证明p是满足i的性质的最大的整数。

  • p满足i的性质,即np=C

p=nkC,则np=C×nnkC×nn,即npC

nmodp=k,k<p,则nk<p×(C+1),即np<C+1

因此np=C

  • p是使得ni=C成立的最大的数。

反证法,假如p不是最大的数,则p+1一定可以使得np+1=C

(p+1)×C=ng(g[0,min{p+1,C})

p×C+C=ng,又p×C=nk

所以C=kg,又k,g[0,C),所以矛盾,得证。

综上,nC即为令ni=C的最大的数

模板

ll H(ll n){
	ll res = 0, l = 1, r;
	while (l <= n){
		r = n / (n / l);
		res = res + (r - l + 1) * (n / l);
		l = r + 1;
	}
	return res;
}

例题

P2261余数之和

给出n,k,计算G(n,k)=i=1nkmodi

数据范围:1n,k109

(懒得自己写题解了,直接cpy洛谷题解orz)由题意得:ans=i=1nkmodi

我们知道,amodb=ab×ab

因此,ans=i=1nki×ki=nki=1ni×ki

首先枚举块的左边界 l,并根据左边界和k 计算出右边界 r

t=kl,分两种情况讨论:

  • t0,则 r=min(kt,n)
  • t=0,则 r=n

右边界有了,每一块的和也就可以计算出了。

每一块的和 = 当前块的 t× 当前块元素个数 × 当前块 i 的平均值 =t×(rl+1)×(l+r)÷2

当前块处理完后,令 l=r+1,开始计算下一块,直到计算至 n。

ll n, m;

void solve(){
	n = read(), m = read();
	ll res = n * m, l = 1, r;
	while (l <= n){
		if (m / l == 0) r = n;
		else r = min(n, m / (m / l));
		res = res - (r - l + 1) * (m / l) * (l + r) / 2;
		l = r + 1;
	}
	printf("%lld\n", res);
}

数论分块往往和莫比乌斯反演结合起来考察,因此进阶的部分放在可能会存在的莫反专题中一起讲解。

posted @   跳岩  阅读(151)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示