关于连续数的约数问题——洛谷P1403和P2424题解

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
  1. 在这道题里,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。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值