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;
}

posted @ 2020-04-02 16:08  bifanwen  阅读(200)  评论(0编辑  收藏  举报