虽然学了,但是我还是不知道它为什么叫除数分块。
题目
我觉得这个没有题目不好讲所以就先上题目。
【余数求和】(洛谷OJ P2261)
给出正整数 n 和 k 计算 G(n,k)=k mod 1+k mod 2+k mod 3+⋯+k mod n的值。 其中 k mod i 表示 k 除以i 的余数。
例如:G(10,5)=5 mod 1+5 mod 2+5 mod 3+5 mod 4+5 mod 5⋯+5 mod 10
=0+1+2+1+0+5+5+5+5+5=29
解题思路
这一道题乍一看可以直接暴力过,但是有一个严重的问题,n,k的范围数量级都是10^9(上面没说),暴力肯定会超时。所以我们可以怎么做呢?(注:以下的 ÷ \div ÷和 / / /都表示数学上的除号,都有使用是为单个式子的美观考虑)
首先,有一个非常有用的转化:
k
k
k
m
o
d
mod
mod
x
x
x=
k
−
⌊
k
x
⌋
∗
x
k-\left \lfloor \frac{k}{x}\right \rfloor*x
k−⌊xk⌋∗x(据说高精取模也用得到)
那么我们就可得到:
G
(
n
,
k
)
G\left ( n,k \right )
G(n,k)=
∑
x
=
1
n
(
k
−
⌊
k
x
⌋
∗
x
)
\sum_{x=1}^{n}(k-\left \lfloor \frac{k}{x}\right \rfloor*x)
∑x=1n(k−⌊xk⌋∗x)=
n
∗
k
−
∑
x
=
1
n
(
⌊
k
x
⌋
∗
x
)
n*k-\sum_{x=1}^{n}(\left \lfloor \frac{k}{x}\right \rfloor*x)
n∗k−∑x=1n(⌊xk⌋∗x)
设
g
(
x
)
=
⌊
k
÷
⌊
k
x
⌋
⌋
g\left ( x \right )=\left \lfloor k\div \left \lfloor \frac{k}{x}\right \rfloor \right \rfloor
g(x)=⌊k÷⌊xk⌋⌋,
x
∈
[
1
,
n
]
x\in \left [ 1,n \right ]
x∈[1,n]
则有
g
(
x
)
≥
⌊
k
÷
(
k
x
)
⌋
=
x
g\left ( x \right )\geq \left \lfloor k\div \left ( \frac{k}{x}\right ) \right \rfloor= x
g(x)≥⌊k÷(xk)⌋=x,证明了
g
(
x
)
g\left ( x \right )
g(x)不小于x
由此可以推出
⌊
k
/
g
(
x
)
⌋
⩽
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor\leqslant \left \lfloor k/x \right \rfloor
⌊k/g(x)⌋⩽⌊k/x⌋
又因为
⌊
k
/
g
(
x
)
⌋
=
⌊
k
/
⌊
k
/
⌊
k
x
⌋
⌋
⌋
≥
⌊
k
/
(
k
/
⌊
k
x
⌋
)
⌋
=
⌊
k
/
k
∗
⌊
k
x
⌋
⌋
=
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor= \left \lfloor k/\left \lfloor k/\left \lfloor \frac{k}{x} \right \rfloor \right \rfloor \right \rfloor\geq \left \lfloor k/\left ( k/\left \lfloor \frac{k}{x} \right \rfloor \right ) \right \rfloor= \left \lfloor k/k*\left \lfloor \frac{k}{x} \right \rfloor \right \rfloor= \left \lfloor k/x \right \rfloor
⌊k/g(x)⌋=⌊k/⌊k/⌊xk⌋⌋⌋≥⌊k/(k/⌊xk⌋)⌋=⌊k/k∗⌊xk⌋⌋=⌊k/x⌋
所以,通过以上
⌊
k
/
g
(
x
)
⌋
⩽
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor\leqslant \left \lfloor k/x \right \rfloor
⌊k/g(x)⌋⩽⌊k/x⌋,
⌊
k
/
g
(
x
)
⌋
⩾
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor\geqslant \left \lfloor k/x \right \rfloor
⌊k/g(x)⌋⩾⌊k/x⌋两式,我们可以得出
⌊
k
/
g
(
x
)
⌋
=
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor=\left \lfloor k/x \right \rfloor
⌊k/g(x)⌋=⌊k/x⌋
得出这个式子的意义在于,对于
∀
x
∈
[
x
,
g
(
x
)
]
\forall x\in \left [ x,g\left ( x \right ) \right ]
∀x∈[x,g(x)],都有
⌊
k
/
g
(
x
)
⌋
=
⌊
k
/
x
⌋
\left \lfloor k/g\left ( x \right ) \right \rfloor=\left \lfloor k/x \right \rfloor
⌊k/g(x)⌋=⌊k/x⌋
这样我们就可以根据
⌊
k
/
x
⌋
\left \lfloor k/x \right \rfloor
⌊k/x⌋的值把n分成若干块,至于具体是多少块,根据大佬的题解所说
⌊
k
/
x
⌋
\left \lfloor k/x \right \rfloor
⌊k/x⌋的取值大概有
k
\sqrt{k}
k个(具体为什么我作为一个小菜鸡是真的不知道),所以也就大概分成
k
\sqrt{k}
k块就行,所以不用担心复杂度的问题。
代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long n,k,ans,g;
int main()
{
scanf("%lld%lld",&n,&k);
ans=n*k;
for (long long i=1,j;i<=n;i=j+1)//把上面说的x换成i,i和j分别作为左右边界
{
g=k/i;
if (!g)j=n;//当i大于k时直接把右边界跳到最末尾
else j=min(n,k/g);//n小于等于k时,怕j一下子跳出界了
ans-=g*(i+j)*(j-i+1)/2; //每个一块中的数都构成等差数列
}
printf("%lld",ans);
}