LOJ #125. 除数函数求和 2 题解
[CSDN同步]
简要题意:
求:
\[\sum_{i=1}^n 2 \sigma_2(i) + 3 \sigma_1(i) + 5 i
\]
其中 \(\sigma_k(x) = \sum_{i=1}^x i^k [x \% i ==0]\),即 \(x\) 所有因数的 \(k\) 次方和。
推式子:
\[\sum_{i=1}^n 2 \sigma_2(i) + 3 \sigma_1(i) + 5i
\]
\[= \sum_{i=1}^ n (2i^2 + 3i + 5) \times \lfloor \frac{n}{i} \rfloor
\]
(计算每个因子 \(i\) 对其它数的贡献)
然后你发现这个式子和整除分块很像。
如果不会可以去 浅谈整除分块 学习一下呦。
好,现在会了 整除分块,你发现你需要高效计算的是:
\[\sum_{i=l}^r (2i^2 + 3i+5)
\]
\[= 2 \times \sum_{i=l}^r i^2 + 3 \times \sum_{i=l}^r i + 5 \times \sum_{i=l}^r 1
\]
\[= 2 \times (\sum_{i=1}^r i^2 - \sum_{i=1}^{l-1} i^2) + 3 \times (\sum_{i=1}^r i - \sum_{i=1}^{l-1} i) + 5 \times (\sum_{i=1}^r 1 - \sum_{i=1}^{l-1} 1)
\]
\[= 2 \times ( \frac{r \times (r+1) \times (2r+1)}{6} - \frac{l \times (l-1) \times (2l-1)}{6})+ 3 \times (\frac{r \times (r+1)}{2} - \frac{l \times (l-1)}{2}) + 5 \times (r-l+1)
\]
然后你发现这可以 \(O(1)\) 计算。(学会小学数学公式真有用~)
时间复杂度:\(O(\sqrt{n})\)
实际得分:\(100pts\).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=998244353;
inline ll read(){char ch=getchar();ll f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
ll n,ans=0;
inline ll count(ll l,ll r) {
__int128 x=(((__int128)r*(r+1)*(2*r+1) - (__int128)(l-1)*l*(2*l-1))/3)%MOD; //平方
x=(x<0)?(x+MOD):x;
__int128 y=((__int128)(3*(l+r)*(r-l+1))/2)%MOD; //一次项
y=(y<0)?(y+MOD):y;
__int128 z=(5*(r-l+1))%MOD; //常数项
z=(z<0)?(z+MOD):z;
__int128 tot=(((__int128)(x+y+z)%MOD)*(n/l))%MOD;
return (ll)(tot<0)?(tot+MOD):tot;
} //好事多模
int main(){
n=read();
ll l=1,r;
for(l=1;l<=n;l=r+1) { //整除分块模板
r=n/(n/l);
ans=(ans+count(l,r))%MOD;
} printf("%lld\n",ans);
return 0;
}
简易的代码胜过复杂的说教。