数论分块
整除定理
整除定理事实上是两个式子:
⌊
a
b
c
⌋
=
⌊
⌊
a
b
⌋
c
⌋
\left\lfloor\frac a {bc}\right\rfloor=\left\lfloor\frac{\lfloor\frac a b\rfloor}c\right\rfloor
⌊bca⌋=⌊c⌊ba⌋⌋
⌈ a b c ⌉ = ⌈ ⌈ a b ⌉ c ⌉ \left\lceil\frac a {bc}\right\rceil=\left\lceil\frac{\lceil\frac a b\rceil}c\right\rceil ⌈bca⌉=⌈c⌈ba⌉⌉
a , b , c ∈ N a,b,c\in\mathbb{N} a,b,c∈N
商定理
∀ n ∈ N + : ∣ { ⌊ n i ⌋ ∣ i ∈ [ 1 , n ] ∩ Z } ∣ = O ( n ) \forall n\in\mathbb{N}^+:\left|\left\{{\left\lfloor{\frac n i}\right\rfloor}|i\in[1,n]\cap\mathbb{Z}\right\}\right|=O(\sqrt n) ∀n∈N+: {⌊in⌋∣i∈[1,n]∩Z} =O(n)
也就是说: ⌊ n i ⌋ \left\lfloor{\frac n i}\right\rfloor ⌊in⌋至多有 O ( n ) O(\sqrt n) O(n)种取值。
这很显然,因为对于
i
≤
n
i\leq \sqrt n
i≤n,至多有
n
\sqrt n
n个
i
i
i,
⌊
n
i
⌋
\left\lfloor{\frac n i}\right\rfloor
⌊in⌋至多有
n
\sqrt n
n种取值。
对于
i
>
n
i>\sqrt n
i>n,
1
≤
n
i
<
n
1\leq\frac n i <\sqrt n
1≤in<n,也至多有
O
(
n
)
O(\sqrt n)
O(n)种取值。
方法一
整除分块用于快速求以下和式:
∑
i
=
1
n
⌊
n
i
⌋
\overset{n}{\underset{i=1}\sum}\left\lfloor{\frac n i}\right\rfloor
i=1∑n⌊in⌋
我们注意到, ⌊ n i ⌋ \left\lfloor{\frac n i}\right\rfloor ⌊in⌋至多有 O ( n ) O(\sqrt n) O(n)种取值,而且显然相同的取值分布于连续的块内。
当我们计算到了一个块 x x x内时,一开始必然处于块的左端点 l l l,我们只需要快速计算出块的右端点 r r r,即可快速计算块内的贡献。
根据结论
r
=
n
/
(
n
/
l
)
r=n/(n/l)
r=n/(n/l),我们可以快速计算右端点,因此可以整除分块。
结论以及证明在下面。
因此可以整除分块:
#include<iostream>
using namespace std;
long long calc(long long n) {
long long sum=0;
for(long long l=1,r;l<=n;l=r+1)
r=n/(n/l),
sum+=n/l*(r-l+1);
return sum;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin>>T;
while(T--) {
int x;
cin>>x;
cout<<calc(x)<<endl;
}
}
或:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
long long calc(long long n) {
long long sum=0;
for(long long l,r=n;r>=1;r=l-1) {
l=(n/(n/r+1))+1;
sum+=n/l*(r-l+1);
}
return sum;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin>>T;
while(T--) {
int x;
cin>>x;
cout<<calc(x)<<endl;
}
}
从左端点计算右端点是非常容易的,也是普遍使用的整除分块写法。
向下取整分块
结论:对于 i i i处于权值为 ⌊ n i ⌋ {\left\lfloor{\frac ni}\right\rfloor} ⌊in⌋的块内,块对应的定义域为 i ∈ [ 1 + ⌊ n ⌊ n i ⌋ + 1 ⌋ , ⌊ n ⌊ n i ⌋ ⌋ ] i\in\left[1+{\left\lfloor{\frac{n}{\left\lfloor{\frac ni}\right\rfloor+1}}\right\rfloor},{\left\lfloor{\frac n{\left\lfloor{\frac ni}\right\rfloor}}\right\rfloor}\right] i∈[1+⌊⌊in⌋+1n⌋,⌊⌊in⌋n⌋]
证明:
我们注意到:
⌊
n
i
⌋
≤
n
i
<
⌊
n
i
⌋
+
1
{\left\lfloor{\frac ni}\right\rfloor}\leq\frac ni<{\left\lfloor{\frac ni}\right\rfloor}+1
⌊in⌋≤in<⌊in⌋+1,这个条件是充要的,即:
R
n
i
=
[
⌊
n
i
⌋
,
⌊
n
i
⌋
+
1
)
R_\frac ni=\left[{\left\lfloor{\frac ni}\right\rfloor},{\left\lfloor{\frac ni}\right\rfloor}+1\right)
Rin=[⌊in⌋,⌊in⌋+1),其中
R
R
R表示值域。
我们对这个条件做不等式的充分必要变换(即不存在不等式放缩),得到:
{
i
≤
n
⌊
n
i
⌋
n
⌊
n
i
⌋
+
1
<
i
\left\{\begin{matrix} i\leq\frac n{\left\lfloor{\frac ni}\right\rfloor}\\ \frac{n}{{\left\lfloor{\frac ni}\right\rfloor}+1}<i \end{matrix}\right.
{i≤⌊in⌋n⌊in⌋+1n<i
对第二个不等式做处理,得到:
{
i
≤
n
⌊
n
i
⌋
1
+
⌊
n
⌊
n
i
⌋
+
1
⌋
≤
i
\left\{\begin{matrix} i\leq\frac n{\left\lfloor{\frac ni}\right\rfloor}\\ 1+\left\lfloor\frac{n}{{\left\lfloor{\frac ni}\right\rfloor}+1}\right\rfloor\leq i \end{matrix}\right.
⎩
⎨
⎧i≤⌊in⌋n1+⌊⌊in⌋+1n⌋≤i
因为
i
∈
Z
:
i\in\mathbb{Z}:
i∈Z:
{
i
≤
⌊
n
⌊
n
i
⌋
⌋
1
+
⌊
n
⌊
n
i
⌋
+
1
⌋
≤
i
\left\{\begin{matrix} i\leq\left\lfloor\frac n{\left\lfloor{\frac ni}\right\rfloor}\right\rfloor\\ 1+\left\lfloor\frac{n}{{\left\lfloor{\frac ni}\right\rfloor}+1}\right\rfloor\leq i \end{matrix}\right.
⎩
⎨
⎧i≤⌊⌊in⌋n⌋1+⌊⌊in⌋+1n⌋≤i
QED.
向上取整分块
结论:对于 i i i处于权值为 ⌈ n i ⌉ {\left\lceil{\frac ni}\right\rceil} ⌈in⌉的块内,块对应的定义域为 i ∈ [ ⌈ n ⌈ n i ⌉ ⌉ , ⌈ n ⌈ n i ⌉ − 1 ⌉ − 1 ] i\in\left[{\left\lceil{\frac n{\left\lceil{\frac ni}\right\rceil}}\right\rceil},{\left\lceil{\frac n{\left\lceil{\frac ni}\right\rceil-1}}\right\rceil}-1\right] i∈[⌈⌈in⌉n⌉,⌈⌈in⌉−1n⌉−1]
证明:
⌈
n
i
⌉
−
1
<
n
i
≤
⌈
n
i
⌉
{\left\lceil{\frac ni}\right\rceil}-1<\frac ni\leq{\left\lceil{\frac ni}\right\rceil}
⌈in⌉−1<in≤⌈in⌉
则:
{
n
⌈
n
i
⌉
≤
i
i
<
n
⌈
n
i
⌉
−
1
\left\{\begin{matrix} \frac n{\left\lceil{\frac ni}\right\rceil}\leq i\\ i<\frac n{{\left\lceil{\frac ni}\right\rceil}-1} \end{matrix}\right.
{⌈in⌉n≤ii<⌈in⌉−1n
即:
{
⌈
n
⌈
n
i
⌉
⌉
≤
i
i
≤
⌈
n
⌈
n
i
⌉
−
1
⌉
−
1
\left\{\begin{matrix} \left\lceil\frac n{\left\lceil{\frac ni}\right\rceil}\right\rceil\leq i\\ i\leq\left\lceil\frac n{{\left\lceil{\frac ni}\right\rceil}-1}\right\rceil-1 \end{matrix}\right.
⎩
⎨
⎧⌈⌈in⌉n⌉≤ii≤⌈⌈in⌉−1n⌉−1
这个条件是充要的,证毕。
方法二
此方法仅供参考。
求以下和式:
∑
i
=
1
n
⌊
n
i
⌋
\overset{n}{\underset{i=1}\sum}\left\lfloor{\frac n i}\right\rfloor
i=1∑n⌊in⌋
假设目前在
i
i
i位置,我们考虑构造带余除法
n
=
p
⋅
i
+
q
n= p\cdot i+q
n=p⋅i+q,其中
p
p
p则为
⌊
n
i
⌋
\left\lfloor\frac n i\right\rfloor
⌊in⌋。
注意
i
i
i为除数,
p
p
p为整除的结果,
q
q
q为余数。
若
i
i
i向左移动1次,则
n
=
p
⋅
(
i
−
1
)
+
(
q
+
p
)
n=p\cdot (i-1)+(q+p)
n=p⋅(i−1)+(q+p)。
向左移动
k
k
k次,则
n
=
p
⋅
(
i
−
k
)
+
(
q
+
k
⋅
p
)
n=p\cdot (i-k)+(q+k\cdot p)
n=p⋅(i−k)+(q+k⋅p)
p
p
p发生改变,当且仅当
i
−
k
≤
q
+
k
⋅
p
i-k\leq q+k\cdot p
i−k≤q+k⋅p,也就是
i
−
q
≤
k
(
1
+
⌊
n
i
⌋
)
i-q\leq k\left(1+\left\lfloor\frac n i\right\rfloor\right)
i−q≤k(1+⌊in⌋)
因而:
k
≥
i
−
q
n
/
i
+
1
k\geq\frac {i-q}{n/i+1}
k≥n/i+1i−q
注意到
q
q
q是余数:
k
≥
i
−
n
m
o
d
i
n
/
i
+
1
k\geq\frac {i-n\;mod\;i}{n/i+1}
k≥n/i+1i−nmodi
我们应该取到最小的
k
k
k,只有这样才使得
k
k
k是块长:
k
=
⌈
i
−
n
m
o
d
i
n
/
i
+
1
⌉
k=\left\lceil\frac {i-n\;mod\;i}{n/i+1}\right\rceil
k=⌈n/i+1i−nmodi⌉
代码很细节:
#include<iostream>
#include<cmath>
using namespace std;
long long H(long long n) {
long long sum=0;
for(long long i=n,k;i>=1;i-=k) {
k=ceil(double(i-n%i)/(n/i+1));
sum+=n/i*k;
}
return sum;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin>>T;
while(T--) {
long long x;
cin>>x;
cout<<H(x)<<endl;
}
return 0;
}
通过这种方法可以求出其他情况下的范围,但是我懒得推了。
更复杂情况:
若要计算:
∑
i
=
1
n
f
(
i
)
⌊
n
i
⌋
\overset{n}{\underset{i=1}\sum}f(i)\left\lfloor\frac n i\right\rfloor
i=1∑nf(i)⌊in⌋,则初始化出
f
f
f函数的前缀和,记作
g
g
g,再分块计算即可,也就是这样:
∑
i
=
1
n
f
(
i
)
⌊
n
i
⌋
=
∑
l
,
r
(
g
(
r
)
−
g
(
l
−
1
)
)
⌊
n
l
⌋
\overset{n}{\underset{i=1}\sum}f(i)\left\lfloor\frac n i\right\rfloor={\underset{l,r}\sum}(g(r)-g(l-1))\left\lfloor\frac n l\right\rfloor
i=1∑nf(i)⌊in⌋=l,r∑(g(r)−g(l−1))⌊ln⌋
后记
其实还有一些像是这样的东西: ∑ n i = 1 ⌊ n i 3 ⌋ \underset{i=1}{\overset{n}\sum}\left\lfloor\frac n {i^3}\right\rfloor i=1∑n⌊i3n⌋,但是这个不在我们今天讨论的范围之内了。
于是皆大欢喜。