洛谷 P2424 约数和

时空限制

1s / 128MB

题目背景

Smart最近沉迷于对约数的研究中。

题目描述

对于一个数X,函数 f(X) 表示 X 所有约数的和。例如:f(6)=1+2+3+6=12。对于一个 X ,Smart可以很快的算出f(X)。现在的问题是,给定两个正整数 X,Y(X<Y) ,Smart希望尽快地算出 f(X)+f(X+1)++f(Y) 的值,你能帮助Smart算出这个值吗?

输入输出格式

输入格式:

输入文件仅一行,两个正整数 X Y(X<Y),表示需要计算 f(X)+f(X+1)++f(Y)

输出格式:

输出只有一行,为 f(X)+f(X+1)++f(Y) 的值。

输入输出样例

输入样例#1:
2 4
输出样例#1:
14
输入样例#2:
123 321
输出样例#2:
72543

说明

对于 20% 的数据有 1XY105

对于 60% 的数据有 1XY1107

对于 100% 的数据有 1XY2109


solution

  • f(x) 用数学方式表示一下就是 f(x)=d|xd

  • ans=i=xyf(i)=i=xyd|id

  • 那我们就可以直接枚举然后累加就可以了。

  • 但这样的时间复杂度是 O(i=xyi) ,会 TLE

  • 所以换一种思路,枚举约数

  • 考虑 1n 中有几个数是 d 的倍数

  • 假如1n中存在 d 的倍数,那这个数肯定可以表示为 kd(kN+)

  • k 的范围可以再简化一下,1kdn1knd

  • 也就是说从 1n 中把 d 的倍数单独拿出来,那就是d,2d,3d.....ndd

  • 所以 1n d 的倍数的个数就是 nd

  • 求出 y 的个数,再减去 x1 的个数,也就是 xy 的个数,这个是比较好想的,所以我就不详细说了。

  • 这样 i=1nf(i) 就可以表示为 i=1n(nii)

  • ans=i=1y(yii)i=1x1(x1ii)

  • 这种做法时间复杂度是 O(y) ,还是会 TLE

  • 再看 i=1n(nii)

  • 只看 ni ,胡乱找个数列出来 ni(1in) 的值

  • 12 为例,列出来是 12,6,4,3,2,2,1,1,1,1,1,1 ,第 i 个数表示ni的值

  • 发现这里面有些数是重复的,考虑能不能把这些重复的一次算出来

  • 把那些相同的值用区间来表示,那只要求出左右端点 l,r 来就好了

  • l 比较好求,观察上面的数列,l 就是上一个 r 1,初始 l=1

  • r 怎么求呢?其实很简单

  • r=n/(n/l)

  • l 是那个数列的下标,所以 (n/l) 就是约数,那 r 就显然了,如果不知道为什么,那就再看一遍“1n中有几个数是 d 的倍数”。

  • l r 都知道了, 那答案呢?

  • ans+=约数*约数的个数

  • 约数 =n/l
  • 约数的个数 =i=lri ,用等差数列求和公式表示一下就是 (l+r)(rl+1)/2

  • ans+=(n/l)(l+r)(rl+1)/2

  • 然后就愉快的AC啦!

code

比题解不知道短到那里去的代码

#include<cstdio>
using namespace std;
typedef long long ll;

ll sum(int n) {
    if(n<=1) return n;
    ll ans=0;
    for(ll l=1,r;l<=n;l=r+1) {
        r=n/(n/l);
        ans+=(n/l)*(l+r)*(r-l+1)/2;
    }
    return ans;
}

int main() {
    int x,y;
    scanf("%d%d",&x,&y);
    printf("%lld\n",sum(y)-sum(x-1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值