初涉数论分块
数论分块:应该算是一类思想
什么是数论分块
我对数论分块的理解就是:在一类要统计的数学题中,由于是单调的,故存在使得。于是只要找到这段区间就可以节省计算区间内每一个函数值的时间开销。
时间复杂度大抵是的?
数论分块入门题
【】例一:bzoj1968: [Ahoi2005]COMMON 约数研究
Description

Input
只有一行一个整数 N(0 < N < 1000000)。
Output
只有一行输出,为整数M,即f(1)到f(N)的累加和。
题目分析
答案即为的所有约数个数和。
我们知道换种形式答案就是。
那么暴力算法来了:所以我们
1 for (int i=1; i<=n; i++) 2 ans += n/i;
就好了。
由于,所以这个的算法是能够过去的。但是不行!这个是数论分块的板子题,我们怎么能够止步于的算法呢?
若用表示,显然,并且是并不严格单调的。
自然而然地想到,在求的同时能不能够求出呢?
这里有一个结论,,下面来证明这个结论。
我们有 ,因此得到
故是满足条件的最大值。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 4 int n; 5 ll ans; 6 7 int main() 8 { 9 register int i,j; 10 scanf("%d",&n); 11 for (i=1; i<=n; i=j+1) 12 j = n/(n/i), 13 ans += 1ll*(j-i+1)*(n/i); 14 printf("%lld\n",ans); 15 return 0; 16 }
【变式】例二:
题目描述
给定两个整数l和r。
对于任意x,满足 l≤x≤r,把x的所有约数全部写下来。
对于每个写下来的数,只保留最高位的那个数码。求[1,9]中每个数码出现的次数。
数据范围
对于100%的数据:
题目分析
这里略有不同的是,每个因数只保留最高位的数码,相当于就是答案贡献加在了不同的地方上。
很显然的是这个答案是可减的,于是我们就可以用类似前缀和的思想依次处理。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 4 int l,r; 5 6 ll work(ll x, ll a, ll b) 7 { 8 ll ret = 0, j = 0; 9 for (ll i=a; i<=b; i=j+1) 10 j = std::min(x/(x/i), b), 11 ret += 1ll*(j-i+1)*(x/i); 12 return ret; 13 } 14 ll get(ll x, ll p) 15 { 16 ll ret = 0, t = 1; 17 for (; x>=p; t*=10, p*=10) 18 ret += work(x, p, std::min(x, p+t-1)); 19 return ret; 20 } 21 int main() 22 { 23 scanf("%d%d",&l,&r); 24 for (int i=1; i<=9; i++) 25 printf("%lld\n",get(r, i)-get(l-1, i)); 26 return 0; 27 }
这里表示个数中,因数最高位为的答案总数。例如,那么最高位为的数一定是,这样的数。
之后的则表示之间是因数的个数。这一步就转变成了的模型,于是就可以用数论分块做了。
至此看上去很奇怪的这道题就转化成了那个普通模型了。
【变式】例三:1257: [CQOI2007]余数之和
Description
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值
其中k mod i表示k除以i的余数。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
Input
输入仅一行,包含两个整数n, k。
1<=n ,k<=10^9
Output
输出仅一行,即j(n, k)。
题目分析
这里的mod操作虽然不单调递增,看上去好像很玄学好像要转化成其他模型的样子,但是我们把式子变化一下:
于是我们发现又回到基本的模型了!
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 4 ll n,k,ans; 5 6 int main() 7 { 8 register int i,j; 9 scanf("%lld%lld",&n,&k); 10 ans = n*k; 11 for (i=1; i<=n; i=j+1) 12 { 13 if (!(k/i)) break; 14 j = std::min(k/(k/i), n); 15 ans -= 1ll*(j+i)*(j-i+1)/2*(k/i); 16 } 17 printf("%lld\n",ans); 18 return 0; 19 }
END
∑i=1n(K∑i⌊ni⌋∑i=1n(K
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现