libreoj 125 除数函数求和2 题解

博客解析了libreoj 125题的解决方案,主要讨论如何计算i从1到n的2σ2(i)+3σ1(i)+5σ0(i),其中σk(x)表示x所有因数的k次方和。通过反向思考,枚举每个因数对答案的贡献,然后利用整除分块的方法,实现了O(n)的时间复杂度解题。

博客观赏效果更佳

题意简述

给定 n n n,求 ∑ i = 1 n 2 σ 2 ( i ) + 3 σ 1 ( i ) + 5 σ 0 ( i ) \sum\limits_{i=1}^{n} 2\sigma_2(i)+3\sigma_1(i)+5\sigma_0(i) i=1n2σ2(i)+3σ1(i)+5σ0(i)

n < = 1 e 9 n<=1e9 n<=1e9

其中 σ k ( x ) = x \sigma_k(x)=x σk(x)=x 所有因数的 k k k 次方和。

思路

我们反过来想,枚举每个因数对答案产生了多少贡献。

显然,一个因数 d d d 对答案产生的贡献就是 2 d 2 + 3 d + 5 2d^2+3d+5 2d2+3d+5

那么有多少 i i i 算到了这个 d d d 呢?只要是 d d d 的倍数,都能算到,所以一共有 ⌊ n d ⌋ \lfloor \dfrac{n}{d}\rfloor dn 个。

答案变成了 ∑ d = 1 n ( 2 d 2 + 3 d + 5 ) ⌊ n d ⌋ \sum\limits_{d=1}^{n} (2d^2+3d+5)\lfloor \dfrac{n}{d}\rfloor d=1n(2d2+3d+5)dn

整除分块,一次 O ( n ) O(\sqrt{n}) O(n )

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define int long long 
    #define mod 998244353
    #define i2  499122177
    #define i6  166374059
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define p_b push_back
    #define sz(a) ((int)a.size())
    #define iter(a,p) (a.begin()+p)
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Rd(int cnt,...)
    {
        va_list args;
        va_start(args,cnt);
        F(i,1,cnt) 
        {
            int* x=va_arg(args,int*);R1(*x);
        }
        va_end(args);
    }
    int n;
    void Input()
    {
        R1(n);
    }
    int s1(int x) {x%=mod; return x*(x+1)%mod*i2%mod;}
    int s2(int x) {x%=mod; return x*(x+1)%mod*(2*x+1)%mod*i6%mod;}
    int S(int x)  {return 5*x%mod+3*s1(x)%mod+2*s2(x)%mod;}
    void Soviet()
    {
        int ans=0;
        for(int l=1,r;l<=n;l=r+1)
        {
            r=n/(n/l);
            ans+=(n/l)%mod*(S(r)-S(l-1)+mod)%mod;
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
    #undef int //long long 
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值