P1403
规定f(x)为x的约数个数,例如f(6)=4,因为6的约数有1、2、3、6这4个。对于从1到n这n个数,我们要求f(1)+f(2)+…+f(n)等于多少。
1.我们不每个数每个数的求,而是对这n个数整体来看。我们知道这n个数所有的约数最小是1,最大是n。所以我们可以从1枚举到n,对于其中的i,n/i表示这n个数的所有约数中i的数量。这个也好理解,n/i就表示1到n中有几个数可以整除i,也就是有几个数有i这个约数。
- 所以代码可以写成:
int ans=0;
for(int i=1; i<=n; i++)
ans += n/i;
cout<<ans<<endl;
- 这样的话时间复杂度是O(n)。还可以优化:
例如当n=20,从1到20这20个数字中,约数1的个数为20/1=20,2的个数为10,3的个数为6,4的个数为5,5的个数为4,6的个数为3,7、8、9、10的个数均为2,11–20的个数均为1。代码可以这样改进:
int ans=0;
for (int i=1, j; i<=n; i=j+1)
{
j = n/(n/i);
ans += n/i*(j-i+1);
}
cout<<ans<<endl;
如何理解呢?例如对于n=20,20个数字中约数7、8、9、10的个数均为2,当i=7的时候,n/i=2,那么个数为2的约数还有哪些呢?可以设个数为2的最大的约数为j,那么n/j=2=n/i,所以说 j = n/(n/i)。此时一共有j-i+1个约数,它们的个数都一样,为n/i,所以ans要加上n/i*(j-i+1),然后下一次继续处理j+1这个约数即可,中间那些就不需要处理了。
P2424
- 在这道题里,f(x)变成了x的所有约数的和,并且要求从n到m这一串数字的f(n)+f(n+1)+…+f(m)的和。和上一题类似:
long long ans=0, i;
for (i=1; i<=n; i++)
ans += i*(n\i);
cout<<ans<<endl;
- n/i表示1到n这n个数字的所有约数中i的个数,根据题意ans要加上个数乘这个约数i。同样可以优化:
long long ans=0, i, j;
for (i=1, j; i<=n; i=j+1)
{
j = n/(n/i);
ans += (n/i)*(j-i+1)*(i+j)/2;
}
cout<<ans<<endl;
- n/i表示 i 到 j 这几个约数中每个约数在所有的约数里的个数,j-i+1表示这些个数一样的约数一共有几个,(i+j)/2表示这几个约数的平均大小。还拿n=20来说,i=7,j=10。i 到 j这几个约数他们的个数都是2,所以应该:
ans += 7*2+8*2+9*2+10*2 = 2*(7+8+9+10) = (20/7)*(10-7+1)*(7+10)/2
应该也可以理解。 - 再本题里,只需要分别对n-1和m处理得到两个ans,然后相减就是结果。注意当对n-1处理的时候,n-1等于1的时,ans是1。