整除分块笔记

UVA11526 H(n)

大概题意:有\(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;
}

学习整除时,写了一道题,写完后打开题解突然发现第一篇和上文代码一样。

[AHOI2005]约数研究

经过仔细的思考,我发现了其中的联系。 真相只有一个
\(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]约数研究

posted @ 2023-01-01 21:11  niuerdao  阅读(33)  评论(0)    收藏  举报