一看我感觉是个什么很难的式子……
结果读完了才发现本质太简单。
算法一
完全按照那个题目所说的,真的把质因数分解的结果保留。
最后乘。
时间复杂度: O ( r r ) O(r \sqrt{r}) O(rr).
实际得分: 40 p t s 40pts 40pts.
(实在想不到比这得分更低的算法了)
算法二
机智的发现是个因数枚举。
然后枚举因数。
时间复杂度: O ( r r ) O(r \sqrt{r}) O(rr).
实际得分: 40 p t s 40pts 40pts.
(只是码量少一点)
算法三
推式子。
f x f_x fx 其实就是 x x x 的因数个数。
我们只需分别求出 ∑ i = 1 r f i \sum_{i=1}^r f_i ∑i=1rfi 和 ∑ i = 1 l − 1 f i \sum_{i=1}^{l-1} f_i ∑i=1l−1fi ,再相减即可。
(日常前缀和思路)
∑ i = 1 r f i \sum_{i=1}^r f_i i=1∑rfi
= ∑ i = 1 r ∑ j ∣ i 1 = \sum_{i=1}^r \sum_{j|i} 1 =i=1∑rj∣i∑1
= ∑ i = 1 r ∑ j = 1 i [ j ∣ i ] = \sum_{i=1}^r \sum_{j=1}^i [j|i] =i=1∑rj=1∑i[j∣i]
= ∑ j = 1 r ∑ i = 1 r [ j ∣ i ] = \sum_{j=1}^r \sum_{i=1}^r [j|i] =j=1∑ri=1∑r[j∣i]
(这步的依据是:我们不枚举每个数的因数,而是考虑每个数作为其它因数所产生的贡献)
= ∑ j = 1 r ⌊ r j ⌋ = \sum_{j=1}^r \lfloor \frac{r}{j} \rfloor =j=1∑r⌊jr⌋
(这步的依据是:从 1 1 1 到 n n n 共有 ⌊ r j ⌋ \lfloor \frac{r}{j} \rfloor ⌊jr⌋ 个数是 j j j 的倍数)
然后到这里,我们暴力枚举。
时间复杂度: O ( r ) O(r) O(r).
实际得分: 60 p t s 60pts 60pts.
算法四
暴力枚举个头?
答案摆在面前还在那暴力
明明是整除分块好吧。
不知道整除分块是啥?
OK \texttt{OK} OK,你发现,这题竟然是 整除分块的模板题 。
时间复杂度: O ( r ) O(\sqrt{r}) O(r).
实际得分: 100 p t s 100pts 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;}
int main(){
ll l=read(),r=read();
ll ans=0; l--;
for(ll i=1,t;i<=r;i=t+1) {
t=r/(r/i); ll len=(t-i+1)%MOD;
ans=(ans+len*(r/i)%MOD)%MOD;
} //这是 1~r 的
for(ll i=1,t;i<=l;i=t+1) {
t=l/(l/i); ll len=(t-i+1)%MOD;
ans=(ans-len*(l/i)%MOD+MOD)%MOD; //这是 1~(l-1) 的
//为了防止模出负数,我们加上 MOD 再模
} printf("%lld\n",(ans+MOD)%MOD);
return 0;
}