初涉数论分块

数论分块:应该算是一类思想

什么是数论分块

我对数论分块的理解就是:在一类要统计inf(i)的数学题中,由于f(i)是单调的,故存在x,y[i,j]使得f(x)=f(y)。于是只要找到这段区间就可以节省计算区间内每一个函数值的时间开销。

时间复杂度大抵是O(n)的?

数论分块入门题

ini】例一:bzoj1968: [Ahoi2005]COMMON 约数研究

Description

 

Input

只有一行一个整数 N(0 < N < 1000000)。

Output

只有一行输出,为整数M,即f(1)到f(N)的累加和。
 

 题目分析

答案即为1..x的所有约数个数和。

我们知道换种形式答案就是ini

那么暴力算法来了:所以我们

1 for (int i=1; i<=n; i++)
2     ans += n/i;

就好了。

由于n=106,所以这个O(n)的算法是能够过去的。但是不行!这个是数论分块的板子题,我们怎么能够止步于O(n)的算法呢?

若用g(i)表示ni,显然ans=i=1ng(i),并且g(i)是并不严格单调的。

自然而然地想到,在求g(i)的同时能不能够求出[i,j]呢?

这里有一个结论,j=nni,下面来证明这个结论。

我们有 j=nninni<j+1,因此得到

{ninjnj+1<ni

j是满足条件的最大值。

 

复制代码
 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 }
复制代码

 

ini变式】例二:

题目描述

给定两个整数lr

对于任意x,满足 l≤x≤r,把x的所有约数全部写下来。

对于每个写下来的数,只保留最高位的那个数码。求[1,9]中每个数码出现的次数。

数据范围

对于100%的数据:1lr109


题目分析

这里略有不同的是,每个因数只保留最高位的数码,相当于就是答案贡献加在了不同的地方上。

很显然的是这个答案是可减的,于是我们就可以用类似前缀和的思想依次处理[1,9]

复制代码
 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  }
复制代码

这里get(x,p)表示1..x个数中,因数最高位为p的答案总数。例如p=3,那么最高位为p的数一定是300..399,3000...3999这样的数。

之后的work(x,a,b)则表示a..b之间是x因数的个数。这一步就转变成了ini的模型,于是就可以用数论分块做了。

至此看上去很奇怪的这道题就转化成了那个普通模型了。

 

ini变式】例三: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操作虽然不单调递增,看上去好像很玄学好像要转化成其他模型的样子,但是我们把式子变化一下:

=i=1n(KiKi)=nKi=1Ki

于是我们发现又回到基本的模型了!

复制代码
 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(KiKi)inii=1n(KiKi)

posted @   AntiQuality  阅读(1313)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示