整除分块
大概要是长成这样的就能用到整除分块:
∑
i
=
1
n
⌊
n
i
⌋
\sum_{i=1}^n \left\lfloor \frac ni \right\rfloor
i=1∑n⌊in⌋
计算这个东西用
O
(
n
)
O(n)
O(n) 的复杂度是显然能解决的,但是在某些考大家思维 毒瘤 的题目里面,就会把你这种做法给卡掉。这时候我们就可以用整除分块来把这个东西的复杂度降到
O
(
n
)
O(\sqrt{n})
O(n)。
思想
我们可以通过打表 (证明) 发现许多的
⌊
n
i
⌋
\left\lfloor \frac ni \right\rfloor
⌊in⌋ 的值时相同的,而且相同的值都聚集在一起。再通过一些神奇的方法 (打表) 可以发现对于每一个值相等的块,他的最后一个数的下标就是
⌊
n
⌊
n
i
⌋
⌋
\left\lfloor\frac{n}{\lfloor\frac ni \rfloor}\right\rfloor
⌊⌊in⌋n⌋,有了这个结论之后我们就可有
O
(
n
)
O(\sqrt{n})
O(n) 计算
∑
i
=
1
n
⌊
n
i
⌋
\sum\limits_{i=1}^n \left\lfloor \frac ni \right\rfloor
i=1∑n⌊in⌋ 了。
打表用代码:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
int main(){
freopen("a.out", "w", stdout);
int n = 100;
for(int i = 1; i <= n; i++){
cout << n / i << endl;
}
return 0;
}
1
∼
100
1 \sim 100
1∼100 打出来是这个效果:
100
50
33
25
20
16
14
12
11
10
9
8
7 7
6 6
5 5 5 5
4 4 4 4 4
3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
代码
整除分块求 ∑ i = 1 n ⌊ n i ⌋ \sum\limits_{i=1}^n \left\lfloor \frac ni \right\rfloor i=1∑n⌊in⌋ 的代码。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
int main(){
int n = 100;
int ans = 0;
for(int l = 1, r; l <= n; l = r + 1){
r = n / (n / l);
ans += (n / l) * (r - l + 1);
}
cout << ans << endl;
return 0;
}
证明:
在前面的部分,我们通过打表 + 瞪眼观察法得到了如果一个区间的开头下表是 i i i,那么这个区间的结尾下标就是 ⌊ n ⌊ n i ⌋ ⌋ \left\lfloor\frac{n}{\lfloor\frac ni \rfloor}\right\rfloor ⌊⌊in⌋n⌋,现在我们就来证明一下这个结论。
首先假设一个区间开头为
i
i
i ,且令
k
=
⌊
n
i
⌋
k = \lfloor \frac ni \rfloor
k=⌊in⌋,那么我们就有:
n
=
k
i
+
r
r
∈
[
0
,
i
)
n = ki + r \qquad r \in [0, i)
n=ki+rr∈[0,i)
现在我们又凭空捏造出一个数
d
d
d,让:
k
=
⌊
n
i
+
d
⌋
k = \left\lfloor \frac{n}{i + d} \right\rfloor
k=⌊i+dn⌋
( 这样我们就只需要证明 d 的取值范围就可以了)然后我们就又可以得到:
n
=
k
i
+
k
d
+
r
′
r
′
∈
[
0
,
i
+
d
)
n = ki + kd + r' \qquad r' \in [0, i + d)
n=ki+kd+r′r′∈[0,i+d)
我们联立上下两式:
k
i
+
k
d
+
r
′
=
k
i
+
r
→
k
d
+
r
′
=
r
→
r
′
=
r
−
k
d
≥
0
ki + kd + r' = ki + r \rightarrow kd + r' = r \rightarrow r' = r - kd \geq 0
ki+kd+r′=ki+r→kd+r′=r→r′=r−kd≥0
所以:
r
−
k
d
≥
0
→
r
≥
k
d
→
d
≤
r
k
r - kd \geq 0 \rightarrow r \geq kd \rightarrow d \leq \frac rk
r−kd≥0→r≥kd→d≤kr
这样我们就把 d 的取值范围顺利的算出来了。然后:
d
m
a
x
=
⌊
r
k
⌋
d_{max} = \left\lfloor \frac rk \right\rfloor
dmax=⌊kr⌋
然后我们也得到了 i 的范围(
i
m
a
x
i_{max}
imax 是右端点):
i
m
a
x
=
i
+
d
m
a
x
=
i
+
⌊
r
k
⌋
=
⌊
i
+
r
k
⌋
=
⌊
i
k
+
r
k
⌋
i_{max} = i + d_{max} = i + \left\lfloor \frac rk \right\rfloor = \left\lfloor i + \frac rk \right\rfloor = \left\lfloor \frac {ik+r}k \right\rfloor
imax=i+dmax=i+⌊kr⌋=⌊i+kr⌋=⌊kik+r⌋
又因为
i
k
+
r
=
n
ik + r = n
ik+r=n 且
k
=
⌊
n
i
⌋
k = \lfloor \frac ni \rfloor
k=⌊in⌋,所以:
i
m
a
x
=
⌊
i
k
+
r
k
⌋
=
⌊
n
⌊
n
i
⌋
⌋
i_{max} = \left\lfloor \frac {ik+r}k \right\rfloor = \left\lfloor \frac {n}{\left\lfloor \frac ni \right\rfloor} \right\rfloor
imax=⌊kik+r⌋=⌊⌊in⌋n⌋
然后就证毕啦。