题面
题解
前置知识
本题要求一个奇怪的函数(f(x))的前缀和:
(f(x)=egin{cases} 0(x{in}prime || x=1) \ x的最大素因子(x为合数,且x中最大素因子的次数{geq}2) \ x的次大素因子(x为合数,且x中最大素因子的次数=1) end{cases})
(x{leq}10^{11})。
首先将询问拆分成(calc(r)-calc(l-1))。
考虑Min_25筛。计算(calc(n))时,需要对于n的整除集合中的所有数x,预处理出1~x之间的素数个数(g(x))。
然后统计答案(s(n,i))时,可以将这里的统计范围缩减为合数,因为素数的(f=0)。
假设在(s(n,i))中,已经枚举到了第j个素数的第k次方,那么接下来有两种情况:
- 这个数在除去(pri[j]^k)之后,只剩下一个素数,那么无论这个素数是不是(pri[j]),最终答案一定都是(pri[j])。
- 这个数在除去(pri[j]^k)之后,还剩下一个合数,那么此时的答案还不能确定,需要递归计算(s(n/pri[j],j+1))。
总时间复杂度为(O({frac{n^{frac{3}{4}}}{log n}}))。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register
#define N 320000
ll l,r,n,sqr,dn;
ll ds[2*N+5]; //整除集合
ll id1[N+5],id2[N+5],g[2*N+5]; //g[i]表示1~i的质数个数
inline ll id(ll x){ //ds的反函数
return x <= sqr ? id1[x] : id2[n/x];
}
ll pn;
ll pri[N+5];
bool isp[N+5];
inline void Eular(){
pn = 0;
for(rg ll i = 2;i <= sqr;i++)isp[i] = 1;
for(rg ll i = 2;i <= sqr;i++){
if(isp[i])pri[++pn] = i;
for(rg ll j = 1;i * pri[j] <= sqr;j++){
isp[i*pri[j]] = 0;
if(i % pri[j] == 0)break;
}
}
}
inline ll s(ll n,ll i){ //这里只统计合数,因为质数f=0
if(pri[i] > n)return 0;
ll ans = 0;
for(rg ll j = i;j <= pn && pri[j] * pri[j] <= n;j++){
for(rg ll cur = pri[j];cur * pri[j] <= n;cur *= pri[j]){
ans += pri[j] * (g[id(n/cur)] - j + 1); //最后只剩下一个质数,答案为pri[j]
ans += s(n / cur,j + 1); //最后剩下一个合数
}
}
return ans;
}
inline ll calc(ll x){
n = x;
sqr = (ll)floor(sqrt(n) + 1e-6);
Eular();
dn = 0;
ll L,R = 0;
while(R < n){
L = R + 1,R = n / (n / L);
ds[++dn] = n / L;
if(n / L <= sqr)id1[n/L] = dn;
else id2[R] = dn;
g[dn] = ds[dn] - 1;
}
for(rg ll j = 1;j <= pn;j++)
for(rg ll i = 1;i <= dn && ds[i] >= pri[j] * pri[j];i++)
g[i] -= (g[id(ds[i]/pri[j])] - (j-1));
return s(n,1);
}
int main(){
cin >> l >> r;
cout << calc(r) - calc(l-1) << endl;
return 0;
}