codevs 2606 约数和问题 (数学+分块)

题目描述 Description

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

输入描述 Input Description

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

输出描述 Output Description

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

样例输入 Sample Input

2 4

样例输出 Sample Output

14

数据范围及提示 Data Size & Hint

对于20%的数据有1≤X<Y≤10^5。

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

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

思路:

这道题代码很简单,主要难点是推公式,我们先可以先推出: ans = ∑⌊n/i⌋*i (1<=i<=n,向下取整),解释下这个公式,我们是取1-n的约数和,那么 n/i向下取整也就是1-n中所有可以整除i的数的个数,然后再乘上i就是i这个约数对答案的贡献,i从1-n跑一边便可以算出答案,但是这样会超时的,那么我们需要优化下这个公式,因为是向下去整的那么肯定会有一些连续的数除i后向下取整得到的值一样,我们可以求出这些值的左右边界,将其归为一块,因为⌊n/i⌋(1<=i<=n,)的值一定递增的等差数列,那么我们求出每一个块的左右边界,直接套用等差数列的求和公式,(a1+an)*n/2, 带入l,r就是: (l+r)*(r-l+1)/2,这样就求的了个数之后再乘上权值就好了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long

ll solve(ll x){
    if(x == 1||x == 0) return x;
    ll l = 1,r = 0,ans = 0; //左右边界
    while(l <= x){
        r = x/(x/l); 
        ans += (x/l)*(l+r)*(r-l+1)/2;
        l = r+1;
    }
    return ans;
}

int main()
{
    ll x,y;
    scanf("%lld%lld",&x,&y);
    cout<<solve(y) - solve(x-1)<<endl;
}

实现代码:

原文地址:https://www.cnblogs.com/kls123/p/9943959.html