BZOJ 1968 [Ahoi2005]COMMON 约数研究:数学【思维题】
题目链接: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 }