题目描述
科学家们在Samuel星球上的探险得到了丰富的能源储备,这使得空间站中大型计算机“Samuel II”的长时间运算成为了可能。由于在去年一年的辛苦工作取得了不错的成绩,小联被允许用“Samuel II”进行数学研究。
小联最近在研究和约数有关的问题,他统计每个正数N的约数的个数,并以f(N)来表示。例如12的约数有1、2、3、4、6、12。因此f(12)=6。下表给出了一些f(N)的取值:
f(n)表示n的约数个数,现在给出n,要求求出f(1)到f(n)的总和。
输入输出格式
输入格式:输入一行,一个整数n
输出格式:输出一个整数,表示总和
输入输出样例
3
5
说明
【数据范围】
20%N<=5000
100%N<=1000000
Solution:
本题纯考数学,因为需要求1到n的数所含的各自的约数和,我们可以转换为:1~n中是1的倍数的数有n/1个,是2的倍数的有n/2个,是3的倍数的数有n/3个,…直到n/n,于是直接O(n)扫一遍每次ans+=n/i就ok了。
当然上述的方法可以完美解决本题,但我看到一种适用于更大数据的巧妙优化方法,思路和上面是一样的,但是我们考虑到当i枚举到比较大时,会出现重复的n/i,举个例子:
当n为100时,n/i(1≤i≤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
容易发现出现了许多重复的数,而我们完全没必要重复去算而是直接一次性累加完。
方法是设x=n/i,y=n/x,x表示的是n内i的倍数的个数,y表示n内因子个数为x的最大因子(即i最多枚举到y,每次n/i的值都为x),则显然因子个数为x的i值共y-i+1个,于是ans+=x*(y-i+1),然后i直接变为y+1;
这样去优化的话:复杂度从O(n)变为了O(2*sqrt(n)),在n特别大时,优化效果极其鲜明。
代码:
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 #define il inline 4 #define ll long long 5 using namespace std; 6 const int N=1000005; 7 int n,k; 8 ll ans; 9 int main() 10 { 11 cin>>n; 12 for(int i=1;i<=n;i=k+1){ 13 k=n/(n/i); 14 ans+=n/i*(k-i+1); 15 } 16 cout<<ans; 17 return 0; 18 }