「学习笔记」min25 筛

貌似看懂板子了,那么来更一更

min25筛

求积性函数前缀和,复杂度 (O(frac{n^{0.75}}{log n})),常数比较小

不支持多点求,同时需要满足 (f(prime)) 是关于 (prime) 的低次多项式(很低次)

这个低次比如 (varphi (prime)=prime-1),其实这个东西是个能快速求的多项式就行了

先把 (sum f(i)) 转成质数的和加合数的和,那么这个算法主要是分成两个部分:第一个 ( m{DP}) 和第二个 ( m{DP})

Part I:DP

考虑如下一个 ( m{DP}:)

[ m{g_k(n,j)=sum_{i=1}^n[iin {}{prime}||minp(i)> P_{j}] f_k(i)} ]

表示 (n) 以内质数或者所有最小质因子大于 ( m{Prime_j}) 的合数的点处多项式次数为 (k) 的部分

转移就是删掉最小质因子为 (j) 的数,具体如下:

[g_k(n,j) =egin{cases}g_k(n,j-1)-p_j^k imes(g_k(frac{n}{p_j},j-1)-g_k(p_j-1,j-1))[p_j^2le n]\g_k(n,j-1) [p_j^2>n]end{cases} ]

最后是删掉质数,乘的系数表示贡献,考虑它是完全积性函数,所以直接除不用管互质

关注到转移的时候对于 ([1,n]) 的每个数只关注其 (frac n{ m{Prime}}) 的值,那么先 (sqrt n) 处理出来所有的结果

这个转移同时也只用到了 (sqrt n) 之内的数字,预处理这些通常是可以接受的

Part II: DP

接下来设 (S(n,x)) 表示 (x) 最小质因子大于 (x)(n) 以内的所有的 函数值 之和

转移如下:

[S(n,x)=g(n)-sum_x+sum_{p_k^cle n,k> x} f(p_k^c) imes (S(frac{n}{p_k^c},k)+[c eq 1]) ]

含义其实是比较显然的,注意后面的求和又一次使用了积性函数的性质,非常巧妙

直接递归统计贡献即可,也不需要记忆化

实现

先预处理每个质数处的前缀和和每个 (g_k(x,0)),根据具体含义,这些东西都是可以通过次方前缀和来做出来的

不难发现对于高次自然数幂前缀和,需要伯努利数或者插值,这也就是处理高次的时候会复杂的原因

处理 (g_k(x,0)) 时只处理每个整除区间右端点的函数前缀和,然后做第一部分的 ( m{DP})(直接对着式子板抄即可)

对于第二部分中的 (g(n)-sum_p) 这个直接对着多项式做减法即可,剩下的都是裸的实现,无需赘述

一些细节:

  • 整除分块的数组要开 (2) 倍,原因是 (lfloor frac{n}x floor) 的结果要分成 (le sqrt n)(ge sqrt n) 的两部分

  • 处理 (1) 的贡献通常的办法是都删掉,最后加上一个 (f(1))

代码是模板题 LOJ6053

#include<bits/stdc++.h>
using namespace std;
namespace yspm{
	const int N=2e5+10,mod=1e9+7;
	int fl[N],pri[N],num,tot,bl,n,id1[N],id2[N],g2[N],g1[N],s1[N],val[N]; 
	inline int get(int x){return x<=bl?id1[x]:id2[n/x];}
	inline int calc(int x,int y){
		if(x<=1||pri[y]>x) return 0;
		int pos=get(x),ans=del(del(g2[pos],g1[pos]),(s1[y-1]+1-y+mod)%mod);
		if(y==1) ans=add(ans,2); 
		for(reg int i=y;i<=num&&pri[i]*pri[i]<=x;++i){
			int prod=pri[i];
			for(reg int j=1;prod*pri[i]<=x;prod*=pri[i],++j){
				ans=add(ans,add(mul(calc(x/prod,i+1),pri[i]^j),pri[i]^(j+1)));
			} 
		} return ans;
	}
	signed main(){
		n=read(); bl=sqrt(n);  
		for(reg int i=2;i<=bl;++i){
			if(!fl[i]) pri[++num]=i,s1[num]=add(s1[num-1],i);
			for(reg int j=1;j<=num&&pri[j]*i<=bl;++j){
				fl[i*pri[j]]=1; if(i%pri[j]==0) break;
			} 
		}
		int i2=5e8+4;
		for(reg int l=1,r;l<=n;l=r+1){
			r=(n/(n/l)); val[++tot]=n/l; 
			g1[tot]=del(val[tot]%mod,1); g2[tot]=mul(add(val[tot]%mod,2),mul(del(val[tot]%mod,1),i2));
			if(n/l<=bl) id1[n/l]=tot; else id2[r]=tot; 
		} 
		for(reg int i=1;i<=num;++i){
			for(reg int j=1;pri[i]*pri[i]<=val[j];++j){
				int k=get(val[j]/pri[i]); 
				g1[j]=del(g1[j],del(g1[k],i-1));
				g2[j]=del(g2[j],mul(pri[i],del(g2[k],s1[i-1])));
			} 
		} printf("%lld
",add(1,calc(n,1))); 
		return 0;
	}
} signed main(){return yspm::main();}

例题

LOJ6625

当头一棒

这题目告诉我们 (min25) 筛不一定要写第二部分,(那么这题应该放到 (DP) 杂题……)

考虑合数处的取值是 ( m{minp(i)-2}),那么使用第一部分的 (DP) 能统计出来每个质数作为多少质数的 ( m{minp})

这种题对于博主这种一根筋的废物还是很有用的!

原文地址:https://www.cnblogs.com/yspm/p/14933738.html