1. 程式人生 > 實用技巧 >Min_25篩學習筆記

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})\)

證明咕著。

例題

【模板】Min_25篩

注意\(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);
}