Min_25筛学习笔记

Min_25筛学习笔记

一般套路

前提和目的

对一个积性函数 f ( x ) f(x) f(x),求其前缀和函数 S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^{n}f(i) S(n)=i=1nf(i),并且 f ( p ) f(p) f(p)是一个关于 p p p的多项式, f ( p k ) f(p^k) f(pk)能够快速计算。

实现

P i P_i Pi表示第 i i i个质数, P P P是质数集。

定义如下函数:
s ( n , j ) = ∑ i = 2 n f ( n ) [ min ⁡ p ∣ i p > P j ] g ( n , j ) = ∑ i = 2 n f ( i ) [ i ∈ P ∨ m i n p ∣ i p > P j ] q ( n ) = ∑ P i ≤ n f ( P i ) s(n,j)=\sum_{i=2}^{n}f(n)[\min_{p|i}p>P_j]\\ g(n,j)=\sum_{i=2}^{n}f(i)[i\in P \lor min_{p|i}p>P_j]\\ q(n)=\sum_{P_i\le n}f(P_i) s(n,j)=i=2nf(n)[piminp>Pj]g(n,j)=i=2nf(i)[iPminpip>Pj]q(n)=Pinf(Pi)

函数 s s s

分别计算质数部分和合数部分的值。

质数部分: q ( n ) − ∑ i = 1 j f ( P i ) q(n)-\sum_{i=1}^{j}f(P_i) q(n)i=1jf(Pi)

合数部分:被算入的合数的最小的质数一定大于 P j P_j Pj,那么不妨枚举是 P k P_k Pk并再枚举是 e e e次方即 P k e P_k^e Pke,那么除掉 P k e P_k^e Pke后剩下的部分的质数一定都是大于 P k P_k Pk的,再根据 f ( x ) f(x) f(x)是积性函数可知这部分的贡献就是 ∑ k > j ∑ e = 1 P k e ≤ n f ( P k e ) ( s ( ⌊ n P k e ⌋ , k ) + [ e > 1 ] ) \sum_{k>j}\sum_{e=1}^{P_k^e\le n}f(P_k^e)(s(\lfloor \frac{n}{P_k^e} \rfloor,k)+[e>1]) k>je=1Pkenf(Pke)(s(Pken,k)+[e>1]),这里的 [ e > 1 ] [e>1] [e>1]是因为当 e > 1 e>1 e>1 f ( P k e ) f(P_k^e) f(Pke)也要算入,但是 e = 1 e=1 e=1是在前面的质数部分就已经算入了,所以不用算入。

综上, s ( n , j ) s(n,j) s(n,j)的转移如下:
s ( n , j ) = q ( n ) − ∑ i = 1 j f ( P i ) + ∑ k > j ∑ e = 1 P k e ≤ n f ( P k e ) ( s ( ⌊ n P k e ⌋ , k ) + [ e > 1 ] ) s(n,j)=q(n)-\sum_{i=1}^{j}f(P_i)+\sum_{k>j}\sum_{e=1}^{P_k^e\le n}f(P_k^e)(s(\lfloor \frac{n}{P_k^e} \rfloor,k)+[e>1]) s(n,j)=q(n)i=1jf(Pi)+k>je=1Pkenf(Pke)(s(Pken,k)+[e>1])

函数g

上面需要求 q ( n ) q(n) q(n),这里用 g g g来解决这个问题。

在实现 g g g之前,我们需要构造一个完全积性函数 h ( x ) h(x) h(x),要求 h ( x ) h(x) h(x)在质数处的取值和 f ( x ) f(x) f(x)相同。由于 g g g函数最后会用到的值只有质数的函数值,所以不妨用 h h h代替 f f f

计算函数 g g g时,不妨考虑 g ( n , j − 1 ) − g ( n , j ) g(n,j-1)-g(n,j) g(n,j1)g(n,j),然后从小到大递推。

g ( n , j − 1 ) − g ( n , j ) g(n,j-1)-g(n,j) g(n,j1)g(n,j)实际上就是最小的质因子为 P j P_j Pj且本身不为质数的 x x x h ( x ) h(x) h(x)的和。当 P j 2 > n P_j^2>n Pj2>n g ( n , j − 1 ) = g ( n , j ) g(n,j-1)=g(n,j) g(n,j1)=g(n,j)因为上述的那种 x x x不存在;当 P j 2 ≤ n P_j^2\le n Pj2n时,根据 h ( x ) h(x) h(x)是完全积性函数的性质,可以知道这个就是 h ( P j ) ( g ( ⌊ n P j ⌋ , j − 1 ) − ∑ i = 1 j − 1 h ( P i ) ) h(P_j)(g(\lfloor\frac{n}{P_j}\rfloor,j-1)-\sum_{i=1}^{j-1}h(P_i)) h(Pj)(g(Pjn,j1)i=1j1h(Pi))

综上, g ( n , j ) g(n,j) g(n,j)的转移如下:
g ( n , j ) = { g ( n , j − 1 ) P j 2 > n g ( n , j − 1 ) − h ( P j ) ( g ( ⌊ n P j ⌋ , j − 1 ) − ∑ i = 1 j − 1 h ( P i ) ) P j 2 ≤ n g(n,j)= \begin{cases} g(n,j-1) & P_j^2 >n\\ g(n,j-1)-h(P_j)(g(\lfloor\frac{n}{P_j}\rfloor,j-1)-\sum_{i=1}^{j-1}h(P_i)) & P_j^2\le n \end{cases} g(n,j)={g(n,j1)g(n,j1)h(Pj)(g(Pjn,j1)i=1j1h(Pi))Pj2>nPj2n
初值就是 g ( n , 0 ) = ∑ i = 2 n h ( i ) g(n,0)=\sum_{i=2}^{n}h(i) g(n,0)=i=2nh(i)

时间复杂度

其实可以发现质数只要筛到 n \sqrt{n} n 的级别就可以了,剩下的可以参考数论分块,计算比较繁琐,最后的结论就是 O ( n 3 4 log ⁡ n ) O(\frac{n^{\frac{3}{4}}}{\log n}) O(lognn43)

实现技巧

存函数值其实不用 map \text{map} map,对于 x ≤ n x\le \sqrt{n} xn 可以直接存,对于 x > n x>\sqrt{n} x>n 可以按 n x \frac{n}{x} xn来存。

例题

【模板】Min_25筛

拆开来, f ( p k ) = ( p k ) 2 − p k f(p^k)=(p^k)^2-p^k f(pk)=(pk)2pk,前面的用 h 0 ( x ) = x 2 h_0(x)=x^2 h0(x)=x2,后面的用 h 1 ( x ) = x h_1(x)=x h1(x)=x

确实模板。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5,mod=1e9+7,inv6=166666668,inv2=500000004;
int P[maxn],bz[maxn],tot=0;
int h0[maxn],h1[maxn];
ll num[maxn],cnt=0;
ll n,sq;
struct Array{
	int num1[maxn],num2[maxn];
	int& operator [] (ll x){
		if (x<=sq) return num1[x];
		return num2[n/x];
	}
}g0,g1;
int S(ll n,int j){
	if (P[j]>n) return 0;
	int res=1ll*((1ll*g0[n]-1ll*h0[j]+1ll*mod)%mod-(1ll*g1[n]-1ll*h1[j]+1ll*mod)%mod+1ll*mod)%mod;
	for (int k=j+1;k<=tot && 1ll*P[k]*P[k]<=n;++k){
		ll tmp=P[k];
		for (int e=1;tmp<=n;++e,tmp*=P[k]){
			int ts=(S(n/tmp,k)+(e>1))%mod;
			int tf=1ll*tmp%mod*(tmp%mod-1)%mod;
			res=(1ll*res+1ll*tf*ts%mod)%mod;
		}
	}
	return res;
}
int main(){
	scanf("%lld",&n);sq=sqrt(n);
	for (int i=2;i<=sq;++i){
		if (!bz[i])
			P[++tot]=i,
			h0[tot]=(1ll*h0[tot-1]+1ll*i*i%mod)%mod,
			h1[tot]=(1ll*h1[tot-1]+1ll*i)%mod;
		for (int j=1;j<=tot && 1ll*i*P[j]<=sq;++j){
			bz[i*P[j]]=1;
			if (i%P[j]==0) break;
		}
	}
	for (ll tmp=1;tmp<=n;tmp=n/(n/tmp)+1) num[++cnt]=n/tmp;
	reverse(num+1,num+1+cnt);
	for (int i=1;i<=cnt;++i){
		int t=num[i]%mod;
		g0[num[i]]=1ll*(t+1)%mod*(2ll*t%mod+1)%mod*t%mod*inv6%mod-1;
		g1[num[i]]=1ll*(t+1)*t%mod*inv2%mod-1;
	}
	for (int j=1;j<=tot;++j){
		for (int i=cnt;i>=1;--i)
			if (1ll*P[j]*P[j]<=num[i])
				g0[num[i]]=(1ll*g0[num[i]]-(1ll*g0[num[i]/P[j]]-1ll*h0[j-1]+1ll*mod)%mod*P[j]%mod*P[j]%mod+1ll*mod)%mod,
				g1[num[i]]=(1ll*g1[num[i]]-(1ll*g1[num[i]/P[j]]-1ll*h1[j-1]+1ll*mod)%mod*P[j]%mod+1ll*mod)%mod;
			else break;
	}
	int ans=(S(n,0)+1)%mod;
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值