LightOJ - 1245 Harmonic Number (II) (找规律)

题目大意:给出一个n(1 <= n < 2^31)求出H(n)的结果,H(n)的定义为下:

分析:对于一个n,设 t = n / i:

  满足 t >= 1的有多少个呢?  有 n / 1 个。

  满足 t >= 2的有多少个呢?  有 n / 2 个。

  ……

  满足 t >= k的有多少个呢?  有 n / k 个。

以上结论不难发现,我们再进一步就能发现:

  满足 t == 1 的有 n/1 - n/2 个

  满足 t == 2 的有 n/2 - n/3 个

  ……

  满足 t == k 的有 n/k - n/(k+1) 个

发现这个规律,我们就需要考虑这个 t 枚举到哪呢?t 从1 枚举 到 n 肯定是对的,但是时间上不允许,我们可以改进一下,我们在枚举 t >= i 的时候,我们可以顺便计算出满足 t >= n/i 的有多少个(举个例子,n == 10,t >= 2的有5个,那么t>=5的就有2个)。这样一来时间复杂度就变为O(√n)了,这样就足够了。详情见代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 typedef unsigned long long ULL;
 9 
10 LL H(int n){
11     LL ans = 0;
12     int m = sqrt(n+0.5);
13     for(int i = 2; i <= m; ++i){
14         ans += (LL)(i-1)*(n/(i-1)-n/i);
15         ans += n/(i-1);
16     }
17     ans += m*(n/m - m);
18     ans += n/m;
19     return ans;
20 }
21 
22 int main(){
23     int T, ca = 1;
24     scanf("%d", &T);
25     while(T--){
26         int n;
27         scanf("%d", &n);
28         printf("Case %d: %lld\n", ca++, H(n));
29     }
30     return 0;
31 }
View Code

 

下面介绍一种最简单的写法,也是比较常用的写法,是数论分块一种:

若n为10,则n/i有几种取值呢?可以列出来是:10,5,3,2,1

我们可以从前往后考虑,

n/i >= 10的有多少个呢?  当然是 n/(n/i) = 1   个

n/i >= 5  的有多少个呢?  当然是 n/(n/i) = 2   个

....

n/i >= 1  的有多少个呢?  当然是 n/(n/i) = 10 个

接下来我们的程序就可以这样写。

 1 #include<cstdio>
 2 int main(){
 3   int T,t=1;
 4   scanf("%d",&T);
 5   while(T--){
 6     long long n,l,i=1,r=0;
 7     scanf("%lld",&n);
 8     for(;i<=n;i=l+1)
 9       l=n/(n/i),r+=(l-i+1)*(n/i);
10     printf("Case %d: %lld\n",t++,r);
11   }
12 }

 

posted @ 2018-08-12 13:51  DyastySun  阅读(160)  评论(0编辑  收藏  举报