洛谷 P2424 约数和

来源:互联网 发布:linux vi 搜索关键字 编辑:程序博客网 时间:2024/06/08 16:20

时空限制

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算出这个值吗?

输入输出格式

输入格式:

输入文件仅一行,两个正整数XY(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

  • 所以1nd 的倍数的个数就是 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 就是上一个r1,初始l=1

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

  • r=n/(n/l)

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

  • lr 都知道了, 那答案呢?

  • 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;}