整除分块学习笔记
0.前言
本文为作者放假前の无聊之举……
作者是个菜*,若文章有错误请大佬指正。
食用本文的注意事项如下:
- 粗体字为重要结论或重要步骤。
- 给到的例题为作者抽象出来的模型,与原题不同。若作者找到了原题,作者会把原题链接放在下面,方便大家提交。在每一道题的讲解中,原题将称作“原题”,例题将称作“例题”。
- 若有原题,那么给出的代码为原题的ac代码,若没有原题,给出的代码为例题代码。
- 划掉的部分为同学评价
诋毁或作者突发恶疾,请自动忽略。 - 文章中还有一些作者未解决的问题,这样的问题后面会有“请求大佬指点”,若有大佬知道,请在评论区回复。作者会在第一时间送上最高礼节:关注。
1.整除分块の基本问题
求下面式子的值
这是原题链接(以下此题称基本问题)
- sum = 87
我贴心地帮你把块分号了
通过以上样例我们可以看出,每一个块中都是一个等差数列。 令
- 首项 =
i*q ; - 令
j = \lfloor \frac{n}{q} \rfloor ,即基本问题中块的最后一项,则末项 =j*q ; - 项数很简单,是
j-i+1 ; - 最后,每一个块中的
sum = q*(i+j)*(j-i+1)/2 ;
那么,又到了我们喜闻乐见的代码环节:其实上面的代码改一下就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll T,n;
ll getsum(ll x)
{
ll ans=0;
for (ll i=1,j;i<=x;i=j+1)
{
ll q=x/i;//改了这里
j=x/q;
ans+=q*(i+j)*(j-i+1)/2;//还有这里
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
{
cin>>n;
cout<<getsum(n)<<"\n";
}
return 0;
}
2.例题2
求下面的式子:
这道题有原题呢
如果看出这道题是整除分块,就非常好ban了。那就往整除分块的基本问题上靠,也就是写出带有整除的式子:
把
我们看到了例题1的形式,把求和符号写开,就可以转化为,例1了:
这样这道题就做完了,和例1基本类似,不过有一点小问题需要注意一下,k可能小于n,所以
所以呢,代码要进行一些小小的改动。
又是我们喜闻乐见的代码环节呢~
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k,l,r,ans;
int main()
{
cin>>n>>k;
ans=n*k;
for (int l=1;l<=n;l=r+1)
{
if (k/l==0) break;
r=min(k/(k/l),n);// 改下这里
ans-=(k/l)*(l+r)*(r-l+1)/2;
}
printf("%lld\n",ans);
return 0;
}
3.例题3
求下面式子的值
其中,函数d(x)表示正整数x因子个数。
原题在哪里呀?原题在这里
这里我对原题题面进行了化简,原题中的函数f与本题的函数d的功能是一样的。为什么呢,这里简单证明一下:
我们发现,对于正整数 我们小学二年级学过的排列组合知识,我们发现,每一个
与下文中的
回归正题,我们发现函数
那么代入原式可得
然后我们交换一下求和顺序,将固定i,找
我们发现,第2个求和的意思是所有小于等于n的d的所有的倍数的个数,也就是
代入原式,即
再令
我们欣喜地发现,这就是我们的基本问题ya.
我们进行了以上推导,这道题已经完成了大部分,不过若要通过此题,还有一些小细节要注意,我会在代码的注释部分写出。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll l,r;
ll sum1,sum2;
ll getsum(ll x)
{
ll ans=0;
for (ll i=1,j;i<=x;i=j+1)
{
j=x/(x/i);
ans=(ans+((x/i)%mod*(j-i+1)%mod)%mod)%mod;
}
return ans%mod;
}
int main()
{
ios::sync_with_stdio(0);
cin>>l>>r;
sum1=getsum(l-1),sum2=getsum(r);//细节1:前缀思想
//cout<<sum1<<"\n";
//cout<<sum2<<"\n";
cout<<((sum2-sum1)%mod+mod)%mod;//细节2:取余。
//这个我看了讨论区的提示,我把链接放在下面↓
return 0;
}
感谢大佬の友情提示
这道题虽然结束了,但我还想说点废话。
其实我一开始并不是这么想的。
当我看到d(x)时,我想到
其中,
代入原式可得
然后我们把求和符号写开
第一个求和是
感谢大佬KingGojianOfYue为作者指点迷津。
看到这里你应该已经发现了我的错误,第一步就错了。欧拉函数的定义是小于
4.例题4
求下面式子的值
其中
呵,原题?
有了上一道题的启发,这一道题的思路也很容易得出。
第一步和上题一样:
然后再代入原式,可得:
和上题一样,交换求和顺序,得出下面的式子:上面详细讲了,这里简单写了
然后我们惊喜的发现,这就是例题1的那个式子,然后我们就可以愉快的写代码了。
愉快の代码时刻↓
和上一道例题的原题一样,这道题也需要前缀思想,只是不用取余了。也许双倍经验?
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y;
ll getsum(ll n)
{
ll ans=0;
for (ll i=1,j;i<=n;i=j+1)
{
int q=n/i;
j=n/q;
ans+=q*(i+j)*(j-i+1)/2;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>x>>y;
cout<<getsum(y)-getsum(x-1);
return 0;
}
5.例题5
经常莫比乌斯反演的朋友都知道,在莫比乌斯反演中,经常需要计算形如下面的式子
其中
对于0基础的小盆友,我贴心的把关于莫比乌斯函数的 前置知识 放在下面,大佬可自动跳过。为了大佬跳过,于是有了标题。
1.莫比乌斯函数定义
莫比乌斯函数的定义如下↓
(以上公式摘自deepseek)
没看懂的话我来详细说说后两个:
把x写成算数基本定理的形式
如果所有c都是1,那么是情况2;
如果存在c不是1,那么是情况3。
2.莫比乌斯函数性质
没什么好说的,直接给式子
3.对原式分块
通过我们的细心观察,我们发现
然后我们讲讲算法步骤:
- 对于块的左端点i,计算
q_n= \lfloor \frac{n}{i} \rfloor q_m= \lfloor \frac{m}{i} \rfloor 。 - 算出块的右端点j。
j = min(\lfloor \frac{n}{q_n} \rfloor,\lfloor \frac{m}{q_m} \rfloor) 。 - 求出每一个块的和。也就是
\sum_{x=i}^{j}\ \mu(x)\lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor=\sum_{x=i}^{j}\ \mu(x)q_n q_m 。 - 然后将和加到结果里。
下面又是喜闻乐见的代码环节呢~
由于这篇文章主要介绍整除分块,对于线性筛求出莫比乌斯函数值的前缀和代码中省略不写。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+6;
int s[maxn];//莫比乌斯函数的前缀和
ll n,m;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
ll ans=0;
ll q=min(n,m);
for (ll i=1,j;i<=q;i=j+1)
{
ll qn=n/i;
ll qm=m/i;
j=min(min(n/qn,m/qm),q);
ans+=(s[i]-s[j-1])*qn*qm;
}
cout<<ans;
return 0;
}
4.写在最后……
AI会帮你的——hsy
有一些确实是借助AI写的,不过观众又发现不了
本文主要是整除分块的基础内容,整除分块还有很多有趣的内容,我也许还会继续更新。
最后郑重声明:尽管很简单,但作者假期前赶稿,难免会有错误,有大佬发现了请在评论区指出,我会献上最高礼节的。
第一次写文章,管理大大辛苦了,求通过。
最后的最后还有更新日志
快下课了,我明天接着更……// update on 7.9 离暑假还有3天。完成了文章的主体内容并写了第一道例题。
写了一会,我要去做题// update on 7.10 离暑假还有2天。做题任务还没完成,写了第第二道例题。
写了一下午,快吃饭了。// update on 7.11 距离暑假的时间已经可以用小时计算了,距离暑假还有21小时。爆肝了两道的例题,并修改了前面的部分内容。
刷了一晚上水贴,又聊了一会儿天 // 还是7.11 不过距离放假还有18小时,今天周杰伦是不是开演唱会?算了不能再玩了,我要去做题了,可是我要放假。写完了最后一道例题和结尾。
我要放假 // update on 7.12 离放假还有最后1小时。最后审查一遍,并增加部分细节。
// 2025.11.27 时间过得好快呀,更正了部分已知错误,解决了原来没有解决的问题,求管理大大快速通过。