概述
求下列式子的值: ∑ i = 1 n n m o d i . \sum_{i=1}^n n\ mod\ i. i=1∑nn mod i.
其中, 1 ≤ n ≤ 1 0 9 . 1\leq n \leq 10^9. 1≤n≤109.
----------------------------------------
#思路
我们可以将
n
m
o
d
i
n\ mod\ i
n mod i写成
n
−
⌊
n
i
⌋
×
i
n-\lfloor \frac{n}{i}\rfloor \times i
n−⌊in⌋×i,那么原式就变成了:
A
n
s
=
∑
i
=
1
n
n
m
o
d
i
=
∑
i
=
1
n
n
−
⌊
n
i
⌋
×
i
=
(
∑
i
=
1
n
n
)
−
(
∑
i
=
1
n
⌊
n
i
⌋
×
i
)
.
Ans=\sum_{i=1}^nn\ mod\ i=\sum_{i=1}^nn-\lfloor \frac{n}{i}\rfloor \times i=(\sum_{i=1}^nn)-(\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor \times i).
Ans=i=1∑nn mod i=i=1∑nn−⌊in⌋×i=(i=1∑nn)−(i=1∑n⌊in⌋×i).
整理一下: A n s = n 2 − ∑ i = 1 n ⌊ n i ⌋ × i . Ans = n^2-\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor \times i. Ans=n2−i=1∑n⌊in⌋×i.
那么现在的关键就是求后面那个 ∑ \sum ∑的值了。
我们可以观察一下 n = 11 n=11 n=11时,每个 i i i的 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋值: i : 1 2 3 4 5 6 7 8 9 10 11 \ \quad i:\quad1\quad 2\quad 3\quad 4 \quad 5\quad 6 \quad7 \quad8 \quad9 \quad10\quad11 i:1234567891011 ⌊ n i ⌋ : 11 5 3 2 2 1 1 1 1 1 1 \lfloor \frac{n}{i}\rfloor:\ 11\quad 5\quad 3\quad 2\quad 2\quad 1\quad 1\quad 1\quad 1\quad 1\quad 1 ⌊in⌋: 115322111111
观察细致的读者也许发现了,同一个 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋值总是连续出现的,而且出现的次数似乎也有规律?
是的,令 i i i的贡献 ⌊ n i ⌋ = x \lfloor \frac{n}{i}\rfloor =x ⌊in⌋=x,那么 x x x出现的次数为 ⌊ n x ⌋ − ⌊ n x + 1 ⌋ \lfloor \frac{n}{x}\rfloor-\lfloor \frac{n}{x+1}\rfloor ⌊xn⌋−⌊x+1n⌋次。
这个结论可以这么理解:贡献大于等于 x x x的 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋一共有 ⌊ n x ⌋ \lfloor \frac{n}{x}\rfloor ⌊xn⌋个,贡献大于 x x x的 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋一共有 ⌊ n x + 1 ⌋ \lfloor \frac{n}{x+1}\rfloor ⌊x+1n⌋个,两者相减即是贡献等于 x x x的个数了。
所以,我们可以分块计算贡献。对于 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋相等的一个块,我们计算 ∑ ⌊ n i ⌋ × i \sum \lfloor \frac{n}{i}\rfloor \times i ∑⌊in⌋×i可以提出公因数 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋,剩下 ∑ i \sum i ∑i是个等差数列,可以用求和公式直接计算。总复杂度均摊 O ( n ) . O(\sqrt n). O(n).
----------------------------------------
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define For(i,j,k) for(register int i=j; i<=(int)k; ++i)
#define Forr(i,j,k) for(register int i=j; i>=(int)k; --i)
#define INF 0x3f3f3f3f
using namespace std;
ll n, top, v, len, Ans;
int main(){
scanf("%lld", &n);
top = n;
Ans = n*n;
while(top >= 1){
v = n/top;//计算当前位置的贡献.
len = n/v - n/(v+1);//计算当前位置贡献出现的次数.
Ans -= v*(2*top-len+1)*len/2;//更新答案.
top -= len;//更新当前位置.
}
printf("%lld", Ans);
return 0;
}
----------------------------------------
小结
本题重点在于将模运算转化成常见的四则运算,再观察贡献表(姑且这么叫吧)找出每个数的贡献所具有的规律,然后就可以快速计算了。
----------------------------------------
— — w r o t e b y m i r a c l e j z d . \mathscr ——wrote\ by\ miraclejzd. ——wrote by miraclejzd.