数论——数论分块
数论——数论分块
数论分块可以快速的求解形如,
的求和式。
不考虑 \(f,g\) 的复杂度,数论分块的复杂度是 \(\mathcal O(\sqrt n)\) 的。
其思想很好玩哦(
下取整的性质
容易发现,
下文所说个数均为规模,表示大概。
在 \(i\in[1,\sqrt n)\) 的时候,一共只有 \(\sqrt n\) 个式子。
在 \(i\in[\sqrt n,n]\) 的时候,一共只有 \(\sqrt n\) 种不同的结果。
因此,我们可以对此分块,使总复杂度降为 \(\mathcal O(\sqrt n)\)。
数论分块的结论
对于常熟 \(n\),使得
成立的最大 \(i\le j\le n\) 的 \(j\) 为,
于是,我们可以根据左端点,推断出右端点,进而推断出下一个块的左端点。
这就是数论分块的本质。
证明不会。
过程
考虑最简单的形式,
显然我们要处理 \(f\) 的前缀和,记为 \(s\)。
由于后面的东西是块状分布的,因此数论分块。
每次以一块,
随后对于下一块,更新区间左端点,
参考代码,
int solev(int n) {
int l = 1, r, ans = 0;
while (l <= n) {
r = n / (n / l);
ans += (n / l) * (s(r) - s(l - 1));
// ans += (n / l) * calc(l, r);
l = r + 1;
}
return ans;
}
例题一
P3935 Calculating
第一步推式子,题中给出函数 \(f(x)\) 表示 \(x\) 的因数个数,因此答案,
左侧用差分,因此要求,
即标准形式的数论分块,代码,
ll solev(ll n) {
ll l = 1, r, ans = 0;
while (l <= n) {
r = n / (n / l);
ans = (ans + (n / l) * (r - l + 1) % mod) % mod;
l = r + 1;
}
return ans;
}
例题二
P2424 约数和
已经给出了式子,整理,即
左侧用差分,因此要求,
即标准形式的数论分块,代码,
ll calc(int l, int r) {
return ((ll)l + r) * (r - l + 1) / 2;
}
ll solev(int x) {
int l = 1, r;
ll ans = 0;
while (l <= x) {
r = x / (x / l);
ans += calc(l, r) * (x / l);
l = r + 1;
}
return ans;
}
例题三
P2261 [CQOI2007] 余数求和
有点技巧,因为容易发现枚举上界和被除数不统一。
回归数论分块的本质,容易发现其实只需要知道块的左右端点就可以了。
于是也容易得出,我们对右端点取 \(n\) 的 \(\min\),注意除数不为零即可。
代码,
ll calc(int l, int r) {
return ((ll)l + r) * (r - l + 1) / 2;
}
ll solev(int n, int x) {
int l = 1, r;
ll ans = 0;
while (l <= n) {
r = n;
if (x / l) r = min(r, x / (x / l));
ans += calc(l, r) * (x / l);
l = r + 1;
}
return ans;
}
例题四
多维数论分块,题目:[ARC068E] Snuke Line。
容易发现,一个步长 \(d\) 在某个颜色的区间 \([l,r]\) 有贡献,当且仅当存在正整数 \(x\),使得,
根据取整的性质,
我们注意到 \(d\) 是变量,我们可以对于 \(l,r\) 用数论分块求出有贡献的 \(d\) 的取值范围。
差分一下即可,同时注意到数论分块求不到 \([l,r]\) 区间本身的答案,我们手动加上即可。
constexpr int N = 1e5 + 10;
int sum[N];
void Main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
int x, y;
cin >> x >> y;
--x;
int l = 1, r;
while (l <= x) {
r = min(x / (x / l), y / (y / l));
if (x / l < y / l) ++sum[l], --sum[r + 1];
l = r + 1;
}
++sum[x + 1], --sum[y + 1];
}
int ans = 0;
for (int i = 1; i <= m; ++i) {
ans += sum[i];
cout << ans << endl;
}
}
本文来自博客园,作者:RainPPR,转载请注明原文链接:https://www.cnblogs.com/RainPPR/p/18208161
如有侵权请联系我(或 2125773894@qq.com)删除。