题意:
long long H( int n ) {
long long res = 0;
for( int i = 1; i <= n; i++ )
res = res + n / i;
return res;
}
计算上面代码的值。
题解:
我们可以观察10这个数字
他除以1,2,3,4,5,6,7,8,9,10之后分别为10,5,3,2,2,1,1,1,1,1.
那么
1的个数为10/1-10/2;
2的个数为10/2-10/3;
3的个数为10/3-10/4;
4的个数为10/4-10/5;
5的个数为10/5-10-6;
6的个数为10/6-10/7;
7的个数为10/7-10/8;
8的个数为10/8-10/9;
9的个数为10/9-10-10;
10的个数为10/10-10/11;
我们发现从6开始个数都为0;
那么我们可以把时间复杂度降到n/2;
然并卵,该超时还是超时。
但是通过观察,我们发现sqrt(n)到n之间的那一段的商值肯定小于sqrt(n),那么我们根据上面的那个式子可以算出商为i,[1<=i<=sqrt(n)]的个数,然后乘以i,就是sqrt(n)后面对答案的贡献值,然后在循环计算的过程中,我们可以轻松算出前sqrt(n)项的n/i值。加一起即为答案。
因为会碰到n/i=i的情况,那么i便算了两遍。我们需要减去一遍。举个栗子。当n=10,i循环到3时,10/3算了一遍3,(10/3-10/4)*3又算了一遍商为3。所以我们需要减去其中一个。
类似的题目有LightOJ 1098,也是需要优化到sqrt(n)
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <math.h>
using namespace std;
#define maxn 4000005
typedef long long ll;
int main()
{
ll n,m;int t,cas=1;cin>>t;
while(t--)
{
cin>>n;ll ans=0;
for(int i=1;i<=sqrt(n);i++)
{
ans+=n/i;
ans+=(n/i-n/(i+1))*i;
//printf("%d %lld\n",i,ans);
if(n/i==i) ans-=n/i;//有重复需要减去
}
printf("Case %d: %lld\n",cas++,ans);
}
return 0;
}
2的个数为10/2-10/3;
3的个数为10/3-10/4;
4的个数为10/4-10/5;
5的个数为10/5-10-6;