洛谷 2424 约数和

题目:https://www.luogu.org/problemnew/show/P2424
题意:
对于一个数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算出这个值吗?
解法:
数学真是玄学...顺便说一句博客园的数学公式编辑真的垃圾
把f(n)用数学方式表达一下 $sum_{d|x} d$
 那么ans =$sum_{i=a}^b f(i)$ = $sum_{i=a}^b sum_{d|x} d$,然后直接枚举并累加,但是这样会TLE。
可以考虑枚举约数,考虑1~n中有几个数是d的倍数,如果有,可以表示为k*d。易知1<=k*d<=n,所以1<=k<=[n/d]。
也就是说把1~n中所有d的倍数拿出来,有d,2d,3d,......,[n/d]d,所以1~n中d的倍数有[n/d]个
 先求b的个数,然后减去a-1的个数,就是b-a的个数了。这样,$sum_{i=a}^b f(i)$ = $sum_{i=a}^b [n/i]*i$,那么ans = $sum_{i=1}^b [b/i]*i$ - $sum_{i=1}^{a-1} [{a-1}/i]*i$
这样做的时间复杂度为O(b),仍然会TLE。
再看$sum_{i=1}^n [n/i]*i$,只看[n/i],以12为例,列出来是12,6,4,3,2,2,1,1,1,1,1,1,第i个数表示[n/i]的值,这个序列表示的就是每个因数的“贡献”,1贡献12次,2贡献6次......
有些数出现了重复,考虑用区间将这些重复的值一次性算出来。设区间左右端点,L,R,L就是上一个R+1,L初始值为1,R = n/(n/i),(n/i)就是约数,如果不知道为什么,那就再看一遍“1~n中有几个数是 d 的倍数”。
ans+=约数*约数的个数,约数=n/L,约数个数$sum_{i=L}^R i$,用等差数列求和公式表示一下就是(L+R)∗(R−L+1)/2
即ans+=(n/L)∗(L+R)∗(R−L+1)/2

AC:

#include <iostream>
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 a,b;
    cin >> a >> b;
    cout << sum(b)-sum(a-1) <<endl;
    return 0;
}


 

原文地址:https://www.cnblogs.com/zz990728/p/8943505.html