【旧博客搬运+修改】整除分块学习笔记
整除分块学习笔记
题目:
给定\(n, k\)(\(n,k\leq 10^9\)),求$$\sum_{i=1}^n k\bmod i$$
分析:
由取模意义可得:
我们只需要求 \(\sum_{i=1}^n i\times \left\lfloor\frac{k}{i}\right\rfloor\) 就可以了.
分析样例:
\(i\) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
\(\left\lfloor\frac{k}{i}\right\rfloor\) | 5 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
发现\(\left\lfloor\frac{k}{i}\right\rfloor\)取值很少(在\(2\sqrt{k}\)左右):
证明: 对于所有 \(i\le \sqrt{k}\), 最多 \(\sqrt{k}\) 个取值, 对于 \(i\ge \sqrt k\), \(\left\lfloor \frac{k}{i} \right\rfloor \le \sqrt{k}\), 故也只有 \(\sqrt{k}\) 个取值, 总共有 \(2\sqrt k\) 个取值.
我们需要找到一个区间\([l,r]\),满足
那么,若已知区间左端点\(l\),如何求出\(r\)?
设\(p=\left\lfloor\frac{k}{l}\right\rfloor\),即所有\(x\in[l,r]\)的\(\left\lfloor\frac{k}{x}\right\rfloor\)的值
要求区间的右端点,即使\(x\)最大并且满足:
由向下取整定义可得
由\(x\)取最大正整数(区间右端点)可得:
所以区间右端点:$$r=\left\lfloor\frac{k}{\left\lfloor\frac{k}{l}\right\rfloor}\right\rfloor$$
回到开始的式子,我们已经求出了区间,对于区间内的答案,为:
因为区间内的\(\left\lfloor\frac{k}{i}\right\rfloor\)相同,所以可以直接取\(\left\lfloor\frac{k}{l}\right\rfloor\)
最后的答案即为\(nk\)减去所有区间答案的和
(注意:若\(\left\lfloor\frac{k}{l}\right\rfloor=0\),则区间为\([l,\infty]\),实现时设置成\(n\)即可,运算中出现\(r>n\)也需要设置\(r=n\))
若还有不理解,参考代码:
(早期代码太丑了)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans = 0, l = 1, r = 0, n, k;
int main() {
cin >> n >> k;
while(r < n) {
r = (k / l ? min(k / (k / l), n) : n);
ans += (l + r) * (r - l + 1) / 2 * (k / l);
l = r + 1;
}
cout << n * k - ans << endl;
return 0;
}
练习:
给定\(n, m, k\)(\(n, m, k\leq 10^{12}\)),求$$\sum_{i=1}n\sum_{j=1}m \left\lfloor\frac{k}{i}\right\rfloor \left\lfloor\frac{k}{j}\right\rfloor$$
后记:
upd: 终于整理完了所有证明, 我好菜啊.
发现没有多少个题解(也有可能是我没看到)完整的解释了右边界\(r\)的来历,于是花了一个下午(没办法我太弱了\(qwq\))来写出完整过程,过程会有点啰嗦,\(\rm dalao\)不要介意。