zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    网络信息安全攻防学习平台第7题
    深入理解读写锁ReentrantReadWriteLock
    彻底理解ReentrantLock
    (三)应该了解关于并发相关的概念
    (二)并发编程的优缺点
    Linux Makefile多目录的编写
    libcurl 下载上传
    MFC枚举USB设备碰到的一个疑难,还没解决
    MFC一个令人纠心的错误
    如何为你的App获取用户的反馈和5星级评论
  • 原文地址:https://www.cnblogs.com/Leohh/p/7512960.html
Copyright © 2011-2022 走看看