题目描述
一个合数的真因数是指这个数不包括其本身的所有因数,例如6 的正因数有
1; 2; 3; 6,其中真因数有1; 2; 3。一个合数的最大真因数则是这个数的所有真因数中最大
的一个,例如6 的最大真因数为3。
给定正整数l 和r,请你求出l 和r 之间(包括l 和r)所有合数的最大真因数之和。
分析
就是叫你算
∑i=l..r,i为合数 iminfactor(i)
∑
i
=
l
.
.
r
,
i
为
合
数
i
m
i
n
f
a
c
t
o
r
(
i
)
前80分很好拿,基本的筛法即可。
提一提7,8两档,由于我们筛的是合数,那么必定存在根号以内的质因子,我们用根号以内的质因子筛[l,r]的部分即可。
线筛是不可能优化的了,考虑枚举质数去筛的筛法,我们必须要枚举一个质数p的倍数px,是因为不知道px有没有更小的质因子,即被之前的质数筛过了。如果能够优化就好了。
先拆成[1..l-1]和[1..r]。
容斥是不可能容斥的了。分析一下,假如我们算[1..n]一个质数p能筛的倍数px,x必然是小于p的质数都没有筛掉的trunc(n/p)以内的数。那么所有x的和就是trunc(n/p)以内的没有被p筛过的数的和。
考虑一个类似洲阁筛的叫做min25筛的东西。
设f(i,j)表示[2..i]中,除去前j个质数的非自身的倍数,剩下的数的和。也就是说,质数也算在里面。设第j个质数为prime[j].
那么(f(n,j-1)-f(n,j))/prime[j],就是第j个质数筛的那些x的和。
考虑这个东西的性质,尝试快速算。
对于一个f(i,j)
如果
prime[j]2>i
p
r
i
m
e
[
j
]
2
>
i
,很明显j可以-1,因为根本不会筛掉任何数。那么j不比
i√
i
大。
否则,考虑第j个质数筛掉的x*prime[j],我们先把prime[j]除掉,那么x满足
2≤x≤i/prime[j]
2
≤
x
≤
i
/
p
r
i
m
e
[
j
]
,且x的质因子不含前j-1个质数。那么x的和就是
f(⌊iprime[j]⌋,j−1)−sum[j−1]
f
(
⌊
i
p
r
i
m
e
[
j
]
⌋
,
j
−
1
)
−
s
u
m
[
j
−
1
]
,其中sum[j-1]表示前j个质数的和。
那么有递推式
f(i,j)=f(i,j−1)−prime[j]∗(f(⌊iprime[j]⌋,j−1)−sum[j−1])
f
(
i
,
j
)
=
f
(
i
,
j
−
1
)
−
p
r
i
m
e
[
j
]
∗
(
f
(
⌊
i
p
r
i
m
e
[
j
]
⌋
,
j
−
1
)
−
s
u
m
[
j
−
1
]
)
记忆化搜索求f(n,m),其中prime[m]为平方后比n小的最大质数,分析时间复杂度,发现i的取值都能够表示成
⌊nx⌋
⌊
n
x
⌋
的形式,因为
⌊nab⌋=⌊⌊na⌋b⌋
⌊
n
a
b
⌋
=
⌊
⌊
n
a
⌋
b
⌋
。那么状态数就是每个i的j的个数和。也就是
∑i=1..n√ni√ln⌊ni⌋
∑
i
=
1..
n
n
i
l
n
⌊
n
i
⌋
,
O(n34logn)
O
(
n
3
4
l
o
g
n
)
左右。时间复杂度跟这个一样。而实际上,如果记忆化搜索,有的n/i是用不到的。但递推就要用到了。
由于状态数比较多,我们记忆化搜索会很慢,哈希存不下,map带log很慢,所以最好按j分层一层一层递推,具体的,把每个n/i分类,对于i<=sqrt(n)的i,我们按i为下标存编号,其余的按n/i为下标存编号,即像map一样记录映射,然后推即可。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2e5+5,M=205,mo=1e9+7;
ll pri[N],i,j,t,sn,sum[N],tmp,ret,n,df[N],val[N],tt,le,ri,v,siz,f[2][N],s,p;
bool pd[N];
ll ans,cnt,l,r,v1,v2,x,mnf[N];
void predo(ll n)
{
fo(i,2,n)
{
if (!pd[i])
{
pri[++pri[0]]=i;
sum[pri[0]]=pri[pri[0]]+sum[pri[0]-1];
}
fo(j,1,pri[0])
{
if (pri[j]*i>n) break;
t=pri[j]*i;
pd[t]=1;
if (i%pri[j]==0)
break;
}
}
}
ll calc(ll x,ll y)
{
tt=0;
le=1;
while (le<=x)
{
v=x/le;
ri=x/v;
df[++tt]=1ll*v*(v+1)/2-1;
if (v<sn) f[0][v]=tt;
else f[1][le]=tt;
val[tt]=v;
le=ri+1;
}
siz=tt;
fo(j,1,y)
{
ri=1;
while (!(val[tt]/pri[j]/pri[j])) tt--;
fo(i,1,tt)
{
v=val[i]/pri[j];
if (v<sn) p=f[0][v];else p=f[1][n/v];
tmp=df[p]-sum[j-1];
if (val[i]==n)
ret+=tmp;
df[i]=df[i]-1ll*tmp*pri[j];
}
}
return df[1];
}
ll calc(ll n)
{
int j;
ret=0;
sn=trunc(sqrt(n));
fo(j,1,pri[0]) if (pri[j]>sn) break;
j--;
calc(n,j);
return ret;
}
int main()
{
freopen("t1.in","r",stdin);
//freopen("factor.out","w",stdout);
predo(2e5);
scanf("%lld %lld\n",&l,&r);
printf("%lld\n",calc(n=r)-calc(n=(l-1)));
}