刚看到这个题,想到的就是i从1到n遍历,每个i出现做为因子的个数是(n/i),全部加起来就可以了

    代码:

const LL mod=1000000007;
LL ans=0;

int main(){
    LL n;
    cin>>n;
    for(int i=1;i<=n;i++){
        ans+=(((n/i)*i)%mod)*i;
        ans%=mod;
    }
    cout<<ans;
}

这个时间复杂度是10^9,并且循环体里还有除法,必然是过不了的,只有30分


但是有了这个思路,我们就想到是否可以利用公式

1^2+2^2+...+n^2=n*(n+1)*(2*n+1)/6

进行优化呢?(蓝桥杯似乎很喜欢出这种公式的使用,三次方相加公式好像也出过)


利用这个公式,很容易写出这样的代码

const LL mod=1000000007;
const LL am=1000000007LL*6;    //下边有解释
LL ans=0;

int main(){
    LL n;
    cin>>n;
    LL m=n;
    for(int i=1;i<=n;i++){
        m=(n/i);                                //每次计算1~m
        LL temp=(((m*(m+1))%am)*(2*m+1))%am;    //计算1加到m的公式
        temp/=6;
        ans+=temp;
        ans%=mod;
    }
    cout<<ans;
}


因为这个公式有除以6,所以需要进行求逆元计算。有的小伙伴可能不太清楚求逆元是干什么的,简单解释一下就是,我们要计算(b/a)%m,是没办法直接((b%m)/(a%m))的,如果b是几个大数相乘,就没办法直接乘的时候取模,这时候就需要用到逆元了,我们只要求出a在m下的逆元x,则(b*x)%m=(b/a)%m,就可以计算了!逆元可以用扩展欧几里得或者快速幂计算。而这里的am是我《算法笔记》学来的,计算(b/a)%m不用求逆元,直接(b%(a*m))/a就可以得出答案(大家先别急着学这个公式,先把求逆元学会)


但是从这里我们可以看到,复杂度仍然是10^9,不过有了这两个思路,我们可以对(n/10000)以上的数字利用公式计算,然后对1~(n/10000)用第一种思路计算,这样时间复杂度就降到了10^5

#include using namespace std;

typedef long long LL;

const LL mod=1000000007;
const LL am=1000000007LL*6;
LL ans=0;

int main(){
    LL n;
    cin>>n;
    LL m=n;
    for(int i=1;i<=10000;i++){
        m=(n/i);
        if(m==0){        //对n小于10000的情况进行处理
            cout<<ans;
            return 0;
        }
        LL temp=(((m*(m+1))%am)*(2*m+1))%am;
        temp/=6;
        ans+=temp;
        ans%=mod;
    }
    for(int i=1;i<=m;i++){
        ans+=(((n/i-10000)*i)%mod)*i;
        ans%=mod;
    }
    cout<<ans;
}


但是这个代码只得了90分,这就是贪图便宜的错,(b%(a*m))/a虽然写的快,但会被一些莫名的数据卡住,所以还是要先求出逆元,再计算,下面的是一伯分代码

#include using namespace std;

typedef long long LL;

const LL mod=1000000007;
const LL ni=166666668;
LL ans=0;


int main(){
    LL n;
    cin>>n;
    LL m=n;
    for(int i=1;i<=10000;i++){
        m=(n/i);
        if(m==0){
            cout<<ans;
            return 0;
        }
        LL temp=(((m*(m+1))%mod)*(2*m+1))%mod;
        temp*=ni;
        temp%=mod;
        ans+=temp;
        ans%=mod;
    }
    for(int i=1;i<=m;i++){
        ans+=(((n/i-10000)*i)%mod)*i;
        ans%=mod;
    }
    cout<<ans;
}



点赞(0)
 

0.0分

5 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 6 条评论

纯牛奶 12月前 回复TA
@jole 爱你
jole 1年前 回复TA
mod = 1000000007
ni = 166666668
ans = 0

n = int(input())
m = n

for i in range(1, 10001):
    m = n // i
    if m == 0:
        print(ans)
        exit(0)
    temp = ((m * (m + 1)) % mod * (2 * m + 1)) % mod
    temp *= ni
    temp %= mod
    ans += temp
    ans %= mod

for i in range(1, m + 1):
    ans += ((n // i - 10000) * i % mod) * i
    ans %= mod

print(ans)把博主的翻译成python了,不好意思,自己等级太低发不了题解,借个楼
捞捞一钓鱼 1年前 回复TA
@破晓 用的是这个公式:1^2+2^2+...+n^2=n*(n+1)*(2*n+1)/6,简单证明可以数学归纳法,百度上也有的
破晓 1年前 回复TA
怎么就能这样写了
LL temp=(((m*(m+1))%am)*(2*m+1))%am;    //计算1加到m的公式
有点懵
破晓 1年前 回复TA
公式怎么用的又不讲
验题君 2年前 回复TA
你这个一伯分腻害了