约数和问题 (codevs2606 && 洛谷2424)

来源:互联网 发布:java gc finalize 编辑:程序博客网 时间:2024/06/05 18:34

P2424 约数和

题目背景

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%的数据有1≤X<Y≤10^5。

对于60%的数据有1≤X<Y≤1*10^7。

对于100%的数据有1≤X<Y≤2*10^9。

 

这题卡了好久,题目很简单很好理解,可推公式的确是件棘手的事

首先想到的方法是用前缀和的思想,ans [l~r]=ans[1~r] – ans[1~l-1]

求1~n的约数和,用的是这种方法:

1:1/2=0           ans=1

2:2/2=1 2/3=0      ans=1*2+2=4

3:3/2=1 3/3=1 3/4=0 ans=1*3+1*2+3=8

4:4/2=2 4/3=1 4/4=1 ans=2*2+1*3+1*4+4=15

原理很明显

n除以一个数a,的数就是1~n这些数中以a为因数的数的个数,再乘以a,就是一个因数和

最后加上n是由于n本身是n的因数

可是糟糕,这种方法会超时,只得了60分

60分代码

#include<iostream>using namespace std;long long l,r,al,ar;long long work(long long a){    long long result=0;    for(long long i=2;i<=a;i++){        if(a/i==0)break;        result+=(a/i)*i;    }    result+=a;    return result;}int main(){    cin>>l>>r;    al=work(l-1);    ar=work(r);    cout<<ar-al;}

无计可施的我在这道题上崩溃了,心塞……

晚饭后,去找元元问这个题,才终于明白过来

这题思路是没毛病的,但是TLE的问题说明代码需要优化加速

用等差数列优化

首先根据之前的发现,以4为例,会发现4/3=1,4/4=1,同样的,在别的数字上也会出现类似的情况,而且数字越大,这种情况的出现越多,我把3,4分别叫做本例中情况的左右边界。而且不难发现这种情况的连续序列中因数都是等差的,差为一,所以可以直接利用等差数列求和

最后我发现,这个代码怎么写都丑。

不要笑我的蠢笨,下面一些辅助理解的东西

等差数列求和公式

#include<iostream>#include<cstdio>using namespace std;long long l,r;long long work(long long x){    long long result=0;    long long d=1,b;//d是左边界,b是右边界     while(1){        b=x/(x/d);        long long some=x/d;        result+=((b+d)*(b-d+1)/2)*some;        d=b+1;        if(x/d==0)break;    }    return result;}int main(){    cin>>l>>r;    long long al=work(l-1);    long long ar=work(r);    cout<<ar-al;}

 

 

0 0
原创粉丝点击