概念
我们考虑这样一个问题:求 \(\sum_{i=1}^{k} \lfloor \dfrac{n}{i} \rfloor\)
我们以 \(n=7,k=7\) 为例子,先画出 \(f(x) = \dfrac{7}{x} \ (1 \leq x \leq 7)\) 的图像
因为我们的取值是向下取整的,我们描出所有可能的取值
注意到所有的点按照取值可以分成若干段
我们可以一次性处理一个段的答案,把整段对答案的贡献加上即可
这就是数论分块
实现
如果要实现整块一起统计,我们需要求出每一块的块头 \(l\) 和块尾 \(r\),则:
\[Ans = \sum_{i=1}^{k} \lfloor \dfrac{n}{i} \rfloor = \sum_{[l,r]} (r-l+1)(\lfloor \dfrac{n}{l} \rfloor) \]注意到每一块的 \(l\) 都可以由上一块的 \(r\) 推出,故我们继续讨论如何在已知 \(l\) 的情况下推出 \(r\)
令 \(t = \lfloor \dfrac{n}{l} \rfloor\) ,容易得到
\[\begin{cases} r = \min(\lfloor \dfrac{n}{t} \rfloor,n) \ \ \ &(t \neq 0) \\ r = n \ \ \ &(t=0) \end{cases} \]直接计算即可
应用
P2261 [CQOI2007]余数求和
先推式子
\[\begin{aligned} G(n,k) &= \sum_{i=1}^{n} k \bmod i \\ &= \sum_{i=1}^{n} k - i \times \lfloor \dfrac{k}{i} \rfloor \\ &= nk - \sum_{i=1}^{n} i \times \lfloor \dfrac{k}{i} \rfloor \end{aligned} \]直接用数论分块解决即可
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll n, k, res;
signed main() {
scanf("%lld%lld", &n, &k);
for (ll l = 1, r; l <= n; l = r + 1) {
r = (k / l) ? min(k / (k / l), n) : n;
res += (k / l) * ((l + r) * (r - l + 1) >> 1);
}
printf("%lld", n * k -res);
return 0;
}
P3935 Calculating
令 \(g(x) = \sum_{i=1}^{n} f(i)\) ,则
\[\sum_{i=l}^{r} = g(r) - g(l-1) \]我们推一下 \(g(x)\)
\[\begin{aligned} g(x) &= \sum_{i=1}^{x} f(i) \\ &= \sum_{i=1}^{x} \sum_{j=1}^{x} [i \mid j] \\ &= \sum_{i=1}^{x} \lfloor \dfrac{x}{i} \rfloor \end{aligned} \]直接用数论分块解决即可
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll Mod=998244353;
ll l, r;
inline ll g(ll n) {
ll res = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
r = (n / l) ? min(n / (n / l), n) : n;
res = (res + (n / l) * (r - l + 1)) % Mod;
}
return res;
}
signed main() {
scanf("%lld%lld", &l, &r);
printf("%lld", ((g(r) - g(l - 1)) % Mod + Mod) % Mod);
return 0;
}
标签:lfloor,分块,数论,dfrac,sum,rfloor,ll
From: https://www.cnblogs.com/wshcl/p/17545884.html