概述
求下列式子的值:∑i=1nn mod i.\sum_{i=1}^n n\ mod\ i.i=1∑nn mod i.
其中,1≤n≤109.1\leq n \leq 10^9.1≤n≤109.
----------------------------------------
#思路
我们可以将n mod in\ mod\ in mod i写成n−⌊ni⌋×in-\lfloor \frac{n}{i}\rfloor \times in−⌊in⌋×i,那么原式就变成了:Ans=∑i=1nn mod i=∑i=1nn−⌊ni⌋×i=(∑i=1nn)−(∑i=1n⌊ni⌋×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).
整理一下:Ans=n2−∑i=1n⌊ni⌋×i.Ans = n^2-\sum_{i=1}^n\lfloor \frac{n}{i}\rfloor \times i.Ans=n2−i=1∑n⌊in⌋×i.
那么现在的关键就是求后面那个∑\sum∑的值了。
我们可以观察一下n=11n=11n=11时,每个iii的⌊ni⌋\lfloor \frac{n}{i}\rfloor⌊in⌋值: i:1234567891011\ \quad i:\quad1\quad 2\quad 3\quad 4 \quad 5\quad 6 \quad7 \quad8 \quad9 \quad10\quad11 i:1234567891011⌊ni⌋: 115322111111\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
观察细致的读者也许发现了,同一个⌊ni⌋\lfloor \frac{n}{i}\rfloor⌊in⌋值总是连续出现的,而且出现的次数似乎也有规律?
是的,令iii的贡献⌊ni⌋=x\lfloor \frac{n}{i}\rfloor =x⌊in⌋=x,那么xxx出现的次数为⌊nx⌋−⌊nx+1⌋\lfloor \frac{n}{x}\rfloor-\lfloor \frac{n}{x+1}\rfloor⌊xn⌋−⌊x+1n⌋次。
这个结论可以这么理解:贡献大于等于xxx的⌊ni⌋\lfloor \frac{n}{i}\rfloor⌊in⌋一共有⌊nx⌋\lfloor \frac{n}{x}\rfloor⌊xn⌋个,贡献大于xxx的⌊ni⌋\lfloor \frac{n}{i}\rfloor⌊in⌋一共有⌊nx+1⌋\lfloor \frac{n}{x+1}\rfloor⌊x+1n⌋个,两者相减即是贡献等于xxx的个数了。
所以,我们可以分块计算贡献。对于⌊ni⌋\lfloor \frac{n}{i}\rfloor⌊in⌋相等的一个块,我们计算∑⌊ni⌋×i\sum \lfloor \frac{n}{i}\rfloor \times i∑⌊in⌋×i可以提出公因数⌊ni⌋\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;
}
----------------------------------------
小结
本题重点在于将模运算转化成常见的四则运算,再观察贡献表(姑且这么叫吧)找出每个数的贡献所具有的规律,然后就可以快速计算了。
----------------------------------------
——wrote by miraclejzd.\mathscr ——wrote\ by\ miraclejzd.——wrote by miraclejzd.