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 }
下面介绍一种最简单的写法,也是比较常用的写法,是数论分块一种:
若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 }