Skip to content

Commit

Permalink
feat(sqrt-decomposition.md): 新增向上取整的数论分块相关内容
Browse files Browse the repository at this point in the history
  • Loading branch information
TOMWT-qwq committed May 15, 2024
1 parent a0ceb96 commit aa622f3
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 12 deletions.
18 changes: 18 additions & 0 deletions docs/math/code/sqrt-decomposition/sqrt-decomposition_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <stdio.h>

long long H(int n) {
long long res = 0; // 储存结果
int l = 1, r; // 块左端点与右端点
while (l <= n) {
r = n / (n / l); // 计算当前块的右端点
res += (r - l + 1) * 1LL *
(n / l); // 累加这一块的贡献到结果中。乘上 1LL 防止溢出
l = r + 1; // 左端点移到下一块
}
return res;
}

main() {
int t, n;
for (scanf("%d", &t); t--; scanf("%d", &n), printf("%lld\n", H(n)));
}
22 changes: 22 additions & 0 deletions docs/math/code/sqrt-decomposition/sqrt-decomposition_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stdio.h>

#include <algorithm>
#define N 100009
int n, a[N], maxn;
long long ans[N];

main() {
scanf("%d", &n);
for (int i = 1; i <= n; scanf("%d", a + i), maxn = std::max(maxn, a[i]), ++i);
for (int i = 0; i < n; ++i)
for (int l = 1, r;; l = r + 1) {
r = std::min(l < a[i] ? (a[i] - 1) / ((a[i] - 1) / l) : N,
l < a[i + 1] ? (a[i + 1] - 1) / ((a[i + 1] - 1) / l)
: N); // 二维数论分块
if (r == N) break;
int x = (a[i + 1] - 1) / l - std::max(a[i] - 1, 0) / l;
if (x > 0) ans[l] += x, ans[r + 1] -= x; // 累加贡献
}
++ans[0]; // ⌈a/l⌉=(a-1)/l+1的式子当a=0时不成立,需要修正
for (int i = 1; i <= maxn; printf("%lld ", ans[i] += ans[i - 1]), ++i);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
10
27
3 changes: 3 additions & 0 deletions docs/math/examples/sqrt-decomposition/sqrt-decomposition_1.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
2
5
10
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10 6 4 3 2 2 1
2 changes: 2 additions & 0 deletions docs/math/examples/sqrt-decomposition/sqrt-decomposition_2.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
3
5 2 7
49 changes: 37 additions & 12 deletions docs/math/number-theory/sqrt-decomposition.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ $$
\left\lfloor\dfrac ni\right\rfloor=\left\lfloor\dfrac nj\right\rfloor
$$

成立的最大的满足 $i\leq j\leq n$ 的 $j$ 的值为 $\left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor$即值 $\left\lfloor\dfrac ni\right\rfloor$ 所在的块的右端点为 $\left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor$。
成立且满足 $i\leq j\leq n$ 的 $j$ 值最大为 $\left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor$即值 $\left\lfloor\dfrac ni\right\rfloor$ 所在块的右端点为 $\left\lfloor\dfrac n{\lfloor\frac ni\rfloor}\right\rfloor$。

??? note "证明"
令 $k=\left\lfloor\dfrac ni\right\rfloor$,可以知道 $k\leq\dfrac ni$。
Expand Down Expand Up @@ -109,17 +109,7 @@ $$

??? note "参考实现"
```cpp
long long H(int n) {
long long res = 0; // 储存结果
int l = 1, r; // 块左端点与右端点
while (l <= n) {
r = n / (n / l); // 计算当前块的右端点
res += (r - l + 1) * 1LL *
(n / l); // 累加这一块的贡献到结果中。乘上 1LL 防止溢出
l = r + 1; // 左端点移到下一块
}
return res;
}
--8<-- "docs/math/code/sqrt-decomposition/sqrt-decomposition_1.cpp"
```

???+ note "N 维数论分块"
Expand All @@ -129,6 +119,41 @@ $$

一般我们用的较多的是二维形式,此时可将代码中 `r = n / (n / i)` 替换成 `r = min(n / (n / i), m / (m / i))`

## 向上取整的数论分块

向上取整与前文所述的向下取整十分类似,但略有区别:

对于常数 $n$,使得式子

$$
\left\lceil\dfrac ni\right\rceil=\left\lceil\dfrac nj\right\rceil
$$

成立且满足 $i\leq j\leq n$ 的 $j$ 值最大为 $\left\lfloor\dfrac{n-1}{\lfloor\frac{n-1}i\rfloor}\right\rfloor$,即值 $\left\lceil\dfrac ni\right\rceil$ 所在块的右端点为 $\left\lfloor\dfrac{n-1}{\lfloor\frac{n-1}i\rfloor}\right\rfloor$。

???+ warning "注意"
当 $i=n$ 时,上式会出现分母为 $0$ 的错误,需要特殊处理。

??? note "证明"
$\left\lceil\dfrac ni\right\rceil=\left\lfloor\dfrac{n-1}i\right\rfloor+1$,可以发现 $n$ 的上取整分块与 $n-1$ 的下取整分块是一样的。

???+ note " 例题:[CF1954E Chain Reaction](https://codeforces.com/contest/1954/problem/E)"
题意:有一排 $n$ 个怪兽,每个怪兽初始血量为 $a_i$,一次攻击会使一段连续的存活的怪兽血量减 $k$,血量不大于 $0$ 视作死亡,对于所有 $k$ 求出击杀所有怪兽所需攻击次数,$n,a_i\leq 10^5$。

??? note "思路"
下面是一种使用二维数论分块的解法:
使用[积木大赛](https://www.luogu.com.cn/problem/P1969)的技巧,令 $a_0=0$,对于某个 $k$,答案就是 $\sum\limits_{i=1}^n\max\left(0,\left\lceil\dfrac{a_i}{k}\right\rceil-\left\lceil\dfrac{a_{i-1}}{k}\right\rceil\right)$。
对于相邻的两个怪兽,使用二维数论分块,分段求出它们对一段 $k$ 的答案的贡献,然后差分累加即可。
复杂度 $O(\sum\sqrt{a_i})$。也存在其他解法。

??? note "实现"
```cpp
--8<-- "docs/math/code/sqrt-decomposition/sqrt-decomposition_2.cpp"
```

## 习题

1. [CQOI2007 余数求和](https://www.luogu.com.cn/problem/P2261)(需要一点转化和特判)
Expand Down

0 comments on commit aa622f3

Please sign in to comment.