[模板]min_25筛
题目
题解
对于原函数 \(f(p^k)=p^k(p^k-1)\),我们可以将其写作 \(f(x)=x^2-x,x\in \Bbb P\),然后,分解成俩完全积性函数:
\[f_1=x \\
f_2=x^2
\]
考虑 \(\tt min\_25\) 筛,有
\[g(i,j)=
\begin{cases}
\sum_{k=2}^i f(k)\quad ,j=0 \\
g(i,j-1)\quad ,i<\text{prime}_j^2 \\
g(i,j-1)-f(\text{prime}_j)\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}f(\text{prime}_k)\right)\quad ,i\ge \text{prime}_j^2
\end{cases}
\]
那么,对于 \(g_0\) 有
\[g_0(i,j)=
\begin{cases}
\sum_{k=2}^i k\quad ,j=0 \\
g(i,j-1)\quad ,i<\text{prime}_j^2 \\
g(i,j-1)-\text{prime}_j\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}\text{prime}_k\right)\quad ,i\ge \text{prime}_j^2
\end{cases}
\]
要处理这个,需要处理出质数的前缀和.
同时,对于 \(g_1\) 有
\[g_1(i,j)=
\begin{cases}
\sum_{k=2}^i k^2\quad ,j=0 \\
g(i,j-1)\quad ,i<\text{prime}_j^2 \\
g(i,j-1)-\text{prime}_j^2\left(g\left(\left\lfloor{i\over \text{prime}_j}\right\rfloor,j-1\right)-\sum_{k=1}^{k<j}\text{prime}_k^2\right)\quad ,i\ge \text{prime}_j^2
\end{cases}
\]
要处理这个,需要处理出质数的平方的前缀和.
由于我们的 \(n\le 10^{10}\),如果直接用其作为下标会炸掉,我们考虑对于所有的 \(\left\lfloor \frac{n}{i}\right\rfloor\) 进行离散:
/** @brief 对于 2\sqrt n 个不同的数值进行离散*/
ll w[maxn+5];int wcnt;
/** @brief 存放两种情况的下标*/
int id[2][maxn+5];
/** @brief
* 得到每种 n/i 的值所归属的块的编号
* 数值大的编号小, 数值小的编号大
*/
inline void hash_id(){
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l);
w[++wcnt]=n/l;
if(w[wcnt]<=sqn)id[0][w[wcnt]]=wcnt;
else id[1][n/w[wcnt]]=wcnt;
}
}
如果我们现在有一个 \(x\),如果我们要得到他的 \(id\),那么我们还需要写一个函数:
/** @brief @p x 归属于哪个编号*/
inline int getid(const ll x){
if(x<=sqn)return id[0][x];
return id[1][n/x];
}
要求 \(S\) 直接照着打就可以了.
代码
const int mod=1e9+7;
const int maxn=2e6;
const int inv6=166666668;
int sie[maxn+5];
int prime[maxn>>2],pcnt;
/** @brief g 有两项,这里分开求*/
int g1[maxn+5],g2[maxn+5];
/** @brief 质数前缀和*/
int ppre[maxn+5];
/** @brief 质数平方的前缀和*/
int pow_ppre[maxn+5];
inline void sieve(const int up){
sie[1]=1;
rep(i,2,up){
if(!sie[i])prime[++pcnt]=i;
for(int j=1;j<=pcnt && i*prime[j]<=up;++j){
sie[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
rep(i,1,pcnt){
ppre[i]=(ppre[i-1]+prime[i])%mod;
pow_ppre[i]=(pow_ppre[i-1]+1ll*prime[i]*prime[i]%mod)%mod;
}
}
ll n;int sqn;
/** @brief 对于 2\sqrt n 个不同的数值进行离散*/
ll w[maxn+5];int wcnt;
/** @brief 存放两种情况的下标*/
int id[2][maxn+5];
/** @brief @p x 归属于哪个编号*/
inline int getid(const ll x){
if(x<=sqn)return id[0][x];
return id[1][n/x];
}
/** @brief
* 得到每种 n/i 的值所归属的块的编号
* 数值大的编号小, 数值小的编号大
*/
inline void hash_id(){
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l);
/** @brief 对于所有 i∈[l,r], 这一块的 n/i 都是 w[wcnt]*/
w[++wcnt]=n/l;
/** @brief w[wcnt] 的编号是多少*/
if(w[wcnt]<=sqn)id[0][w[wcnt]]=wcnt;
else id[1][n/w[wcnt]]=wcnt;
}
}
inline int sum(ll x){x%=mod;
return (1ll*x*(x+1)/2)%mod;
}
inline int squa(ll x){x%=mod;
return 1ll*x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
inline void getg(){
/** @brief g 的初始化, -1 是为了去掉 f(1)*/
rep(i,1,wcnt){
g1[i]=sum(w[i])-1;
g2[i]=squa(w[i])-1;
}
rep(j,1,pcnt){
for(int i=1;i<=wcnt && 1ll*prime[j]*prime[j]<=w[i];++i){
/** @brief 此处应考虑 id 的特性——数值大的编号小, 所以才可以这样写*/
g1[i]=(g1[i]+mod-1ll*prime[j]*(g1[getid(w[i]/prime[j])]+mod-ppre[j-1])%mod)%mod;
g2[i]=(g2[i]+mod-1ll*prime[j]*prime[j]%mod*(g2[getid(w[i]/prime[j])]%mod-pow_ppre[j-1])%mod)%mod;
}
}
}
int S(const ll x,const int j){
if(prime[j]>x)return 0;
int ret=(g2[getid(x)]+mod-g1[getid(x)])%mod;
ret=(ret+mod-(g2[getid(prime[j])]+mod-g1[getid(prime[j])])%mod)%mod;
// 等价形式 : ret=(ret+mod-(pow_ppre[j]+mod-ppre[j])%mod)%mod;
for(int i=j+1;i<=pcnt && 1ll*prime[i]*prime[i]<=x;++i)
for(ll e=1,sp=prime[i];sp<=x;sp*=prime[i],++e)
ret=(ret+1ll*sp%mod*(sp%mod-1)%mod*(S(x/sp,i)+(e>1))%mod)%mod;
return ret;
}
signed main(){
n=readin(1ll);sqn=sqrt(n);
sieve(sqn);
hash_id();
getg();
writc((S(n,0)+1)%mod,'\n');
return 0;
}
用到の小 \(\tt trick\)
十分单纯的 \(\tt min\_25\) 筛板子题.