Min_25篩學習筆記
0、一些定義
- 定義域為正整數集,陪域為複數域的函式被稱為數論函式。
- 設\(f(x)\)是一個數論函式,若對於任意一對互質的正整數\(a,b\),有\(f(ab)=f(a)f(b)\),則稱\(f(x)\)是積性函式。
- 記\(\delta(x)\)為\(x\)的最小質因子,\(P^+(x)\)為\(x\)的最大質因子。
- 記\(cnt(x)\)為\(x\)的質因子個數。
- 記第\(i\)個質數為\(p_i\)。特別地,\(p_0=1\)。
- 記\(\pi(x)\)為不超過\(x\)的質數個數。由素數定理,\(\pi(x)=O(\frac{x}{\log x})\)。
1、min_25篩
論文題:定義\(\sigma_0(n)\)
為\(n\)的正約數個數,求\(S(n)=\sum_{i=1}^n\sigma_0(i^k)\)。\(n,k\leq 10^{10}\)。拓展:給定一個積性函式\(f(x)\),且對於任意質數\(p\)和正整數\(e\),滿足:
- \(f(p)\)是一個關於\(p\)的低次多項式。
- 計算\(f(p^e)\)的複雜度可忽略。
- 求\(S(n)=\sum_{i=1}^nf(i)\)。
解法
令\(f(x)=\sigma_0(x^k)\),容易發現\(f(x)\)是積性函式。
令
\[g(n,i)=\sum_{\substack{2\leq x\leq n\\ \delta(x)> p_i}}f(x) \]
那麼\(S(n)=f(1)+g(n,0)\)。
考慮計算\(g(x)\)的遞推式,將\(x\)分成質數和合數兩個部分,合數部分列舉最小質因子(一定\(\leq \sqrt{n}\)),質數部分單獨計算,於是
\[\begin{align*} g(n,i)&=\sum_{\substack{i< j\leq \pi(\sqrt{n})}} \sum_{\substack{2\leq x\leq n\\ \delta(x)=p_j}}f(x)+\sum_{i< j\leq \pi(n)}f(p_j) \\&=\sum_{\substack{i< j\leq \pi(\sqrt{n})\\ p_j^{e+1}\leq n,e\geq 1}} \left(f(p_j^{e+1})+f(p_j^e)\sum_{\substack{2\leq x\leq \left\lfloor\frac{n}{p_j^e}\right\rfloor\\ \delta(x)> p_j}}f(x)\right)+\sum_{i< j\leq \pi(n)}f(p_j) \\&=\sum_{\substack{i< j\leq \pi(\sqrt{n})\\ p_j^{e+1}\leq n,e\geq 1}} \left(f(p_j^{e+1})+f(p_j^e)g\left(\left\lfloor\frac{n}{p_j^e}\right\rfloor,j\right)\right)+\sum_{1\leq j\leq \pi(n)}f(p_j) -\sum_{1\leq j\leq i}f(p_j)\end{align*}\]
令
\[h(n)=\sum_{1\leq i\leq \pi(n)}f(p_i) \]
那麼
\[g(n,i)=\sum_{\substack{i< j\leq \pi(\sqrt{n})\\ p_j^{e+1}\leq n,e\geq 1}} \left(f(p_j^{e+1})+f(p_j^e)g\left(\left\lfloor\frac{n}{p_j^e}\right\rfloor,j\right)\right)+h(n)-h(p_i) \]
如果能快速計算\(h\),就可以在很低的複雜度裡遞推求出\(g(n,0)\)。注意到\(f\)在質數處的取值為多項式,所以只需要對於非負整數\(s\),計算\(h_s(n)=\sum_{1\leq i\leq \pi(n)}p_i^s\)。注意到\(g(n,i)\)的遞推過程,我們只需要對於\(1,2,\ldots,\lfloor\sqrt{n}\rfloor,\left\lfloor\frac{n}{\lfloor\sqrt{n}\rfloor}\right\rfloor,\ldots,\left\lfloor\frac{n}{2}\right\rfloor,n\)這\(O(\sqrt{n})\)個數求出\(h\)值。
令\(h_s'(n,i)\),為前\(n\)個數,經過\(i\)次埃氏篩後剩下的數的\(s\)次冪之和。那麼\(h_s(n)=h_s'(n,\pi(\sqrt{n}))\)。
對於計算\(h_s'(n,i)\)的遞推式,考慮第\(i\)輪埃氏篩的過程:
- 若\(n<p_i^2\),那麼不會有任何數被篩去,於是\(h_s'(n,i)=h_s'(n,i-1)\)。
- 若\(n\geq p_i^2\),那麼被篩去的數必有質因子\(p_i\),於是應當減去\(p_i^sh_s'\left(\left\lfloor\frac{n}{p_i}\right\rfloor,i-1\right)\)。但是會有最小質因子小於\(p_i\)的數被減去,所以應當加上這部分的貢獻,為\(p_i^sh_s'(p_{i-1},i-1)\)。
那麼
\[h_s'(n,i)=h_s'(n,i-1)-[n\geq p_i^2]p_i^s\left(h_s'\left(\left\lfloor\frac{n}{p_i}\right\rfloor,i-1\right)-h_s'(p_{i-1},i-1)\right) \]
邊界條件為
\[h_s'(n,0)=\sum_{i=2}^ni^s \]
暴力實現即可。
時間複雜度
\(O(\frac{n^\frac{3}{4}}{\log n})\)
證明咕著。
例題
注意\(f\)在質數\(p\)上的取值為\(p^2-p\),於是需要同時求出質數的和和平方和,即\(h_1,h_2\)。然後照著公式實現即可。
#include<cstdio>
#define ll long long
#define P 1000000007
#define inv3 333333336
#define N 200005
ll n;
int prm[N],_prm;
bool vis[N];
inline void sieve(int x){
for(int i=2;i<=x;i++){
if(!vis[i])
prm[++_prm]=i;
for(int j=1;j<=_prm&&i*prm[j]<=x;j++){
vis[i*prm[j]]=1;
if(!(i%prm[j]))
break;
}
}
}
int sq,m;
inline int id(ll x){
return x<=sq?x:m-n/x+1;
}
ll num[N];
int h1[N],h2[N];//h(x)=h2(x)-h1(x),h1,h2開成滾動陣列
inline void get_h(){
while(1ll*sq*sq<=n)
sq++;
sq--;
sieve(sq);
for(int i=1;i<=sq;i++)
num[++m]=i;
for(int i=sq;i;i--)
if(n/i>sq)
num[++m]=n/i;
for(int i=1;i<=m;i++){
int tmp=num[i]%P;
h1[i]=1ll*tmp*(tmp+1)/2%P-1;
h2[i]=1ll*tmp*(tmp+1)/2%P*(2*tmp+1)%P*inv3%P-1;
}//邊界條件
for(int i=1;i<=_prm;i++)
for(int j=m;num[j]>=1ll*prm[i]*prm[i];j--){//注意倒序遞推
int k=id(num[j]/prm[i]);
h1[j]=(h1[j]-1ll*prm[i]*(h1[k]-h1[prm[i-1]]+P)%P+P)%P;
h2[j]=(h2[j]-1ll*prm[i]*prm[i]%P*(h2[k]-h2[prm[i-1]]+P)%P+P)%P;
}
}
inline int g(ll x,int i){
int k=id(x),res=((h2[k]-h1[k]+P)%P-(h2[prm[i]]-h1[prm[i]]+P)%P+P)%P;//h(x)-h(p_i)
for(int j=i+1;j<=_prm&&1ll*prm[j]*prm[j]<=x;j++)
for(ll pe=prm[j];pe*prm[j]<=x;pe=pe*prm[j]){
int tmp=pe%P;
res=(res+1ll*tmp*(tmp-1)%P*g(x/pe,j)%P)%P;//f(p_j^e)*g(x/p_j^e,j)
res=(res+1ll*tmp*prm[j]%P*(1ll*tmp*prm[j]%P-1)%P)%P;//f(p_j^(e+1))
}
return res;
}
int main(){
scanf("%lld",&n);
get_h();
printf("%d\n",((g(n,0))+1)%P);
}