题意:定义函数
f
(
x
)
=
∑
i
=
1
x
[
i
∣
x
]
f(x)= \sum_{i = 1}^x[i|x]
f(x)=∑i=1x[i∣x],即x的因子个数,给定
l
l
l,
r
r
r求
(
∑
i
=
l
r
f
(
i
)
)
%
998244353
(\sum_{i=l}^r{f(i)})\% 998244353
(∑i=lrf(i))%998244353
求和数论题在优化复杂度时经常使用把遍历每个数变成遍历每个因子的套路,是很多题的突破口.
数论分块
就在这里写了,数论分块只是一个简单的工具,就不单独拿出来整理.
给定
n
n
n,求
(
∑
i
=
1
n
⌊
n
i
⌋
)
(\sum_{i=1}^n\lfloor\frac{n}i\rfloor)
(∑i=1n⌊in⌋)%998244353.
n
≤
1
0
12
n≤10^{12}
n≤1012
复杂度
O
(
n
)
O(\sqrt n)
O(n)
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(r-l+1)*(n/l);
ans %= MOD;
}
题解
题目给出的是左右边界,那么我们应该求出一个前缀和来得到答案.
设
g
(
x
)
=
∑
i
=
1
x
f
(
i
)
g(x) = \sum_{i = 1}^xf(i)
g(x)=∑i=1xf(i),那么结果是
g
(
r
)
−
g
(
l
−
1
)
g(r)-g(l-1)
g(r)−g(l−1).
接下来考虑
∑
i
=
1
n
f
(
i
)
\sum_{i = 1}^nf(i)
i=1∑nf(i)
f
(
i
)
f(i)
f(i)已经可以表示为i的因子个数,可以表示为
∑
d
∣
n
1
=
∑
i
=
1
n
[
i
∣
n
]
\sum_{d|n}1 = \sum_{i = 1}^n[i|n]
d∣n∑1=i=1∑n[i∣n]
整合进
g
g
g中
g
(
n
)
=
∑
i
=
1
n
∑
d
∣
n
1
g(n) = \sum_{i = 1}^n\sum_{d|n}1
g(n)=i=1∑nd∣n∑1
接下来要更换枚举项:
这个函数的意思是:
1
∼
n
1\sim n
1∼n每个数的因子个数加起来的总和
我们换个说法:在
1
∼
n
1\sim n
1∼n中是
i
i
i的倍数的数有
x
i
x_i
xi个,求
∑
i
=
1
n
x
i
\sum_{i = 1}^n{x_i}
i=1∑nxi
1
∼
n
1\sim n
1∼n中是
d
d
d的倍数的数有多少个呢?
⌊
n
d
⌋
\lfloor\frac{n}d\rfloor
⌊dn⌋个
∑
i
=
1
n
x
i
=
(
∑
i
=
1
n
⌊
n
i
⌋
=
g
(
n
)
)
\sum_{i = 1}^n{x_i}=(\sum_{i = 1}^n \lfloor\frac{n}i\rfloor = g(n))
i=1∑nxi=(i=1∑n⌊in⌋=g(n))
括号这一部分整除分块,
O
(
n
)
O(\sqrt n)
O(n)解决.
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mod = 998244353;
ll l,r;
ll f(ll x){
ll ans=0;
for(ll l=1,r;l<=x;l=r+1)
{
r=x/(x/l);
ans+=((r-l+1)*(x/l)%mod)%mod;
}
return ans;
}
int main(){
scanf("%lld%lld",&l,&r);
printf("%lld",( (f(r)%mod-f(l-1)%mod+mod)%mod ));
}