题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1968
题意:
设f(x) = x约数的个数。如:12的约数有1,2,3,4,6,12,所以f(12) = 6。
给定n,问你f(1)到f(n)之和。
题解:
好多做法。。。
(1)O(N*sqrt(N))
纯暴力(应该过不了)。
枚举i,sqrt(i)复杂度求出约数个数,更新ans。
不附代码。
(2)O(N*log(N))
若当前枚举到i,则i为i*k的一个约数(k >= 0),dp[i*k]++。
先枚举i,再枚举i*k,复杂度 = n * (1 + 1/2 + 1/3 + 1/4 +...+ 1/n) = N*log(N)
(3)O(N)
转化问题:
设g(x) = [1,n]中x倍数的个数。
ans = ∑ g(i)
显然有g(x) = floor(n/x),O(1)算出。
枚举i,ans += g(i),复杂度O(N)。
(4)O(sqrt(N))
延续(3)的思路。
显然,对于数列g(x),你会发现有一些区间内的数都是一样的。
那么哪些g(x)会是相同的呢?
假如现在枚举到了i。
由于 g(x) = floor(n/i)
所以有 n/i = g(i) ... P(余数)
那么现在想求出这段区间的末尾位置j,即求出满足n/j = g(i) ... P,显然当P(余数)越接近0时,j越大。
所以当P约等于0时,末尾位置j = floor(n/g(i)) = floor(n/floor(n/i))。
所以下一个区间的起始位置为j+1。
所以对于处理的每个i,要将ans += (j-i+1) * g(i)
复杂度 = 不同的floor(n/i)的个数 = sqrt(N)
看下效率差距。。。(从下往上为算法2,3,4)
AC Code(2):
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 1000005 5 6 using namespace std; 7 8 int n; 9 int ans=0; 10 int dp[MAX_N]; 11 12 int main() 13 { 14 cin>>n; 15 memset(dp,0,sizeof(dp)); 16 for(int i=1;i<=n;i++) 17 { 18 for(int j=i;j<=n;j+=i) 19 { 20 dp[j]++; 21 } 22 ans+=dp[i]; 23 } 24 cout<<ans<<endl; 25 }
AC Code(3):
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 5 using namespace std; 6 7 int n; 8 int ans=0; 9 10 int main() 11 { 12 cin>>n; 13 for(int i=1;i<=n;i++) 14 { 15 ans+=n/i; 16 } 17 cout<<ans<<endl; 18 }
AC Code(4):
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 5 using namespace std; 6 7 int n; 8 int ans=0; 9 10 int main() 11 { 12 cin>>n; 13 for(int i=1,j=1;i<=n;i=j+1) 14 { 15 j=n/(n/i); 16 ans+=(j-i+1)*(n/i); 17 } 18 cout<<ans<<endl; 19 }