整除分块笔记
大概题意:有\(T\)组数据,对于每组给出一个整数 \(n\) 。对于每组数据求出 \(\sum_{i=1}^n\left\lfloor\dfrac{n}{i}\right\rfloor\)。
Solution
你看它函数都给出来了,直接抄不好吗
TLE狂喜
众所周知,这题要用分块
正如标题,这题可以看作分块模板
何为分块?
以此题为例,当 \(n=15\) 时,
\(sum=15+7+5+3+3+2+2+1+1+1+1+1+1+1+1\)
可以看出 \(3,2,1\) 是重复出现
而题目给的解法会重复计算这些重复出现的数字
所以这是我们要利用分块的思想来减少计算量
比如此题中 \(2\) 出现了2次,那我们怎么确定 \(2\) 第一次和最后一次出现的位置呢?
还是以\(15\)为例
我们从 \(\left\lfloor\dfrac{15}{1}\right\rfloor\) 开始计算
此时我们已经确定了结果为 \(15\) 的左界,所以我们要寻找右界
观察 \(\left\lfloor\dfrac{n}{i}\right\rfloor\),可以发现 :
当结果为 \(x\) 时一定满足 \(n\geqslant x \times i\)
所以我们可以求得最后一个结果为 \(x\) 的数肯定为 \(n/x\)
但是我们也不能枚举\(x\),因为这样也是 \(O(n)\) 的 肯定会寄
但是我们已经求出了上一个值的的右界
众所周知,左界 \(+1\)就是右界
所以 \(x\) 的另一种求法就是 \(n/左界\)
代码
1#define ll long long //偷懒小技巧
2inline ll f(ll n){ //inline 可以更快
3 ll sum=0;
4 for(ll l=1,r;l<=n;l=r+1){ //左界
5 //开long long避免炸精度
6 r = n / (n / l); //右界
7 sum += (r - l + 1) * (n / l);
8 }
9 return sum;
10}
代码还是自己写有效果,抄一遍也比复制好
双倍经验1
P2261 [CQOI2007]余数求和
温馨提示:这题和上一题几乎一样,就是多了一些细节上的处理。
参考代码:
点我点我
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n, k;
cin>>n>>k;
long long cnt=n*k;
for(long long l=1, r; l<=n; l=r+1){
if (k/l!=0) r=min((k/(k/l)), n);
else r=n;
cnt-=(r+l)*(k/l)*(r-l+1)/2;
}
cout<<cnt;
}
学习整除时,写了一道题,写完后打开题解突然发现第一篇和上文代码一样。
经过仔细的思考,我发现了其中的联系。 真相只有一个
在 \(x\) (\(1\leqslant x\leqslant n\)) 中约数中包含约数 \(i\) (\(1\leqslant i\leqslant n\)) 的数有\(\left\lfloor\dfrac{n}{i}\right\rfloor\)个
你品,你细品
双倍经验2
P2424 约数和
温馨提示:这题也只是多了一些细节。
实在想不出再来点我欧~
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll f(ll n){
if(n<=1) return n;
ll res=0;
for(ll l=1,r;l<=n;l=r+1){
r = n / (n / l);
res += (r - l + 1) * (n / l) * (l + r) / 2;
}
return res;
}
int main(){
ll x,y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", f(y)-f(x-1));
}
Update
2022.9.24:增加 P2424 约数和 和 [AHOI2005]约数研究。

浙公网安备 33010602011771号