Min_25筛详解

本文详细介绍了Min_25筛算法,一种用于解决积性函数前缀和问题的有效方法。通过具体例题,展示了如何利用埃式筛法及动态规划思想优化计算过程,并给出了完整的C++实现代码。

正题

例题:loj6053:简单的函数

Min_25筛是用来解决积性函数前缀和问题的。

这个积性函数要满足下面几个性质:

1.对于一个质数 p p p f ( p ) f(p) f(p) 一定要是关于 p p p 的一个多项式

2.对于一个质数 p p p ,计算 f ( p c ) f(p^c) f(pc) 的时间不能太大。

例题满足上述两个条件:

因为对于一个大于 2 2 2 的质数 p p p ,有 f ( p ) = p − 1 f(p)=p-1 f(p)=p1,而且 f ( p c ) f(p^c) f(pc) 很好计算。

现在只关注质数位置上的值,将 f ( x ) f(x) f(x) 直接看成 f ( x ) = x − 1 f(x)=x-1 f(x)=x1

接下来把 f ( x ) f(x) f(x) 分开成多个函数相加,使得每一个函数都是完全积性的。

可以分成两个函数 f 1 ( x ) = 1 , f 2 ( x ) = x f_1(x)=1,f_2(x)=x f1(x)=1,f2(x)=x

对于每一个 x = ⌊ n i ⌋ x=\lfloor \frac n i \rfloor x=in 先求: ∑ i = 1 x f 1 ( i ) [ i is a prime ] \sum_{i=1}^x f_1(i)[\text{i is a prime}] i=1xf1(i)[i is a prime]

考虑埃式筛法并 D p Dp Dp ,设 g ( n , j ) g(n,j) g(n,j) 为前 n n n 个数,用了前 j j j 个质数进行埃式筛法后的函数值总和。

如果 p j 2 > n p_j^2>n pj2>n 那么说明它不会再筛去任何一个数,所以 g ( n , j ) = g ( n , j − 1 ) g(n,j)=g(n,j-1) g(n,j)=g(n,j1)

否则 g ( n , j ) = g ( n , j − 1 ) − f 1 ( P j ) [ g ( n / P j , j − 1 ) − ∑ i = 1 j − 1 f 1 ( P i ) ] g(n,j)=g(n,j-1)-f_1(P_j)[g(n/P_j,j-1)-\sum_{i=1}^{j-1}f_1(P_i)] g(n,j)=g(n,j1)f1(Pj)[g(n/Pj,j1)i=1j1f1(Pi)]

求出来了 g g g 以后,考虑如何算答案。

S ( n , j ) S(n,j) S(n,j) 表示前n个数中,最小质因子 ≥ P j \geq P_j Pj f ( x ) f(x) f(x) 数的之和,注意这里的 f ( x ) f(x) f(x) 是原来的 f ( x ) f(x) f(x)

那么答案就是 S ( n , 1 ) + 1 S(n,1)+1 S(n,1)+1

有递推式 S ( n , j ) = ( g 2 ( n , ∣ p ∣ ) − g 1 ( n , ∣ p ∣ ) ) − ∑ i = 1 j − 1 f ( p i ) + ∑ k = j p k 2 ≤ n ∑ e = 1 p k e + 1 ≤ n S ( n / p k e , k + 1 ) ∗ f ( p e ) + f ( p e + 1 ) S(n,j)=(g_2(n,|p|)-g_1(n,|p|))-\sum_{i=1}^{j-1}f(p_i)+\sum_{k=j}^{p_k^2\leq n}\sum_{e=1}^{p_k^{e+1}\leq n}S(n/p_k^e,k+1)*f(p^e)+f(p^{e+1}) S(n,j)=(g2(n,p)g1(n,p))i=1j1f(pi)+k=jpk2ne=1pke+1nS(n/pke,k+1)f(pe)+f(pe+1)

首先前面三个式子是算 ≤ n \leq n n 的质数的 f ( x ) f(x) f(x) 值总和。由于小于 p j p_j pj 的质数不算,所以要减去他们的 f f f 值。后面就是枚举一个最小质因子为 p k e p_k^e pke,然后剩下的用 ≥ p k + 1 \geq p_{k+1} pk+1 的质数去填,由于是积性函数,所以乘起来就是答案,再加上一个次方 f ( p k e + 1 ) f(p_k^{e+1}) f(pke+1) 即可。

这里直接递归的时间复杂度就是的 O ( n 3 4 log ⁡ n ) O(\frac{n^{\frac{3}{4}}}{\log n}) O(lognn43) ,上面求 g g g 的时间复杂度也是这个。

常数不知道, 1 0 10 10^{10} 1010的数据跑了 597ms \text{597ms} 597ms

有几个注意的地方:

1.我们筛质数只要筛到 n \sqrt n n 就可以了,因为两个东西都是要求的。

2.如果我递归的时候 p y p_y py 没有被筛到呢?这样的情况至多只会出现一层,就算不特判也对结果没有影响。

#include<bits/stdc++.h>
using namespace std;

long long n;
int m,data;
const long long mod=1e9+7;
bool vis[100010];
int id1[100010],id2[100010];
long long w[100010],g1[100010],g2[100010],sum[100010],p[100010];

void get_prime(){
	data=sqrt(n);
	for(int i=2;i<=data;i++){
		if(!vis[i]) p[++p[0]]=i,sum[p[0]]=(sum[p[0]-1]+i)%mod;
		for(int j=1;i*p[j]<=data;j++){
			vis[i*p[j]]=true;
			if(i%p[j]==0) break; 
		}
	}
}

void get_2g(){
	long long l=1,r,now;
	while(l<=n){
		r=n/(n/l);w[++m]=n/l;
		if(w[m]<=data) id1[w[m]]=m;
		else id2[n/w[m]]=m;
		g1[m]=(w[m]-1)%mod;
		g2[m]=((2+w[m])%mod)*((w[m]-1)%mod)%mod;
		if(g2[m]&1) g2[m]+=mod;g2[m]/=2;
		l=r+1;
	}
	for(int j=1;j<=p[0];j++){
		for(int i=1;i<=m && p[j]*p[j]<=w[i];i++){
			now=w[i]/p[j];
			int id=(now<=data?id1[now]:id2[n/now]);
			g1[i]=(g1[i]-g1[id]+j-1)%mod;(g1[i]+=mod)%=mod;
			g2[i]=(g2[i]-p[j]*(g2[id]-sum[j-1])%mod)%mod;(g2[i]+=mod)%=mod;
		} 
	}
}

long long S(long long x,int y){
	if(x<=1 || p[y]>x) return 0;
	int id=(x<=data?id1[x]:id2[n/x]);
	long long ans=(g2[id]-g1[id]-sum[y-1]+y-1)%mod,p1,p2;(ans+=mod)%=mod;
	if(y==1) (ans+=2)%=mod;
	for(int k=y;k<=p[0] && p[k]*p[k]<=x;k++){
		p1=p[k],p2=p1*p[k];
		for(int e=1;p2<=x;e++,p1=p2,p2*=p[k])
			(ans+=S(x/p1,k+1)*(p[k]^e)%mod+(p[k]^(e+1)))%=mod;
	}
	return ans;
}

int main(){
	scanf("%lld",&n);
	get_prime();get_2g();
	printf("%lld\n",(S(n,1)+1)%mod);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值