Min_25筛学习小计

好菜啊,现在才会Min_25

简单介绍

  • 由Min_25在他的博客中介绍的做法,又称Min_25筛。
  • 用于求积性函数f(n)f(n)的前缀和,其中要求f(p)f(p)可以表示成多项式,并且f(pk)f(p^k)可以快速算出。
  • 能够在O(n0.75log)O(frac{n^{0.75}}{log})的时间内算出。

基本思路

  • 使用DPDP先求出所有素数的f(p)f(p)的前缀和,然后再通过枚举最小的质因子计算出合数的前缀和。

求素数的前缀和

  • 下面的所有除法都为整除。
  • 我们之后要求对所有的m=nim=frac{n}{i}iprime,i<=mf(i)sum_{isubseteq prime,i<=m}f(i)
  • 为了方便计算我们将f(i)f(i)拆成几个完全积性函数的和。使得这些积性函数在素数pp的位置的和与f(i)f(i)一致。
  • 接下来考虑一个完全积性函数f(i)f(i)在素数位置的和怎么求。
  • 那么用容斥,考虑用所有的和减去合数的和。
  • g(n,j)=i=1n[iprimeminp(i)>pri[j]]f(i)g(n,j)=sum_{i=1}^n[isubseteq prime|minp(i)>pri[j]]f(i)
  • g(n,j)g(n,j)g(n,j+1)g(n,j+1)的过程相当于是一个埃氏筛法(即找出nsqrt n下的质数,将它的倍数筛掉,相当于现在筛到的数的最小因子为当前质数)。
  • 那么如果pri[j]2>npri[j]^2>n,则pri[j]pri[j]不能继续筛了,所以:
    g(n,j)=g(n,j1)g(n,j)=g(n,j-1)
  • 否则pri[j]pri[j]可以筛,即为:
    g(n,j)=g(n,j1)pri[j](g(n/pri[j],j1)k<jf(pri[k]))g(n,j)=g(n,j-1)-pri[j](g(n/pri[j],j-1)-sum_{k<j}f(pri[k]))
  • 因为gg里面有小于pri[j]pri[j]的质数,但是它们乘上pri[j]pri[j]的最小因子并不是pri[j]pri[j],不满足当前筛的数的范围——最小质因子为pri[j]pri[j],所以减去。
  • 最后求得g(n,P)g(n,|P|)即为素数的和了。

亿点点细节

  • 一般使用pkp^k作为完全积性函数,初值g(n,0)g(n,0)就是一个自然数幂求和。
  • 这个东西用滚动数组求。
  • 由于要求所有g(ni,P)g(frac{n}{i},|P|),查找的时候可以设一个id1,id2id1,id2,如果i<=sqrt(n)i<=sqrt(n)就直接存在该位置,否则存在n/in/i的位置,可以证明一一对应。然后离散化去储存和转移。
  • 还需要在线筛时预先求出k<jf(pri[k])sum_{k<j}f(pri[k])
  • 至此我们就能求出i<=mf(i)sum_{i<=m}f(i),这里的f(i)f(i)是题意中的函数。
  • 所有计算都不计算1的贡献,因为筛的时候比较方便

求所有数的前缀和

  • S(n,j)=i=1n[minp(i)>=pri[j]]f(i)S(n,j)=sum_{i=1}^n[minp(i)>=pri[j]]f(i)
  • S(n,j)=g(n,P)k<jf(pri[k])+k=jpri[k]2<=nq=1pri[k]q+1<=nS(n/pri[k]q,k+1)+f(pri[k]q+1)S(n,j)=g(n,|P|)-sum_{k<j}f(pri[k])+sum_{k=j}^{pri[k]^2<=n}sum_{q=1}^{pri[k]^{q+1}<=n}S(n/pri[k]^q,k+1)+f(pri[k]^{q+1})
  • 前面的质数的和,后面合数的和也是枚举最小的质因子的次幂去筛它,注意最后一项算不到要补上。
  • 出口即n<pri[j],n=1n<pri[j],n=1返回00
  • 因为整除的数是有限且不重复的,所以S(n,j)S(n,j)并不需要像杜教筛一样记忆化。
  • 听说时间是O(n0.75log)O(frac{n^{0.75}}{log})的,最快能过101210^{12}
  • 相较杜教筛普适性更高了,但是速度也更慢了。
    luoguP3235
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 500005
#define ll long long 
#define mo 1000000007
using namespace std;

ll n,sqr,i,j,k,m,inv6;
ll id1[maxn],id2[maxn],w[maxn];
ll bz[maxn],tot,pri[maxn],sg1[maxn],sg2[maxn];
ll s[maxn],g1[maxn],g2[maxn];

ll ksm(ll x,ll y){
	ll s=1;
	for(;y;y/=2,x=x*x%mo) if (y&1)
		s=s*x%mo;
	return s;
}

void getpri(){
	for(i=2;i<=sqr;i++){
		if (!bz[i]) {
			pri[++tot]=i;
			sg1[tot]=(sg1[tot-1]+i)%mo;
			sg2[tot]=(sg2[tot-1]+1ll*i*i)%mo;
		}
		for(j=1;j<=tot&&i*pri[j]<=sqr;j++){
			bz[i*pri[j]]=1;
			if (i%pri[j]==0) break;
		}
	}
}

void prepare(){
	getpri();
	for(i=1;i<=n;i=j+1){
		j=n/(n/i),w[++m]=n/i;
		if (w[m]<=sqr) id1[w[m]]=m; else
			id2[n/w[m]]=m;
		ll tmp=w[m]%mo; 
		g1[m]=(tmp+1)*tmp/2%mo-1;
		g2[m]=tmp*(tmp+1)%mo*(tmp*2+1)%mo*inv6%mo-1;
	}
	for(j=1;j<=tot;j++){
		for(i=1;i<=m&&pri[j]*pri[j]<=w[i];i++){
			int k=(w[i]/pri[j]<=sqr)?id1[w[i]/pri[j]]:id2[n/(w[i]/pri[j])];	
			(g1[i]-=pri[j]*(g1[k]-sg1[j-1]))%=mo;
			(g2[i]-=pri[j]*pri[j]%mo*(g2[k]-sg2[j-1]))%=mo;
		}
	}
}

ll S(ll x,int j){
	if (x==1||pri[j]>x) return 0;
	int k=(x<=sqr)?id1[x]:id2[n/x];
	ll sum=g2[k]-g1[k]-sg2[j-1]+sg1[j-1];
	for(k=j;k<=tot&&pri[k]*pri[k]<=x;k++){
		ll p=pri[k],q=p%mo;
		while (p*pri[k]<=x){
			sum+=S(x/p,k+1)*(q*(q-1)%mo)%mo;
			p=p*pri[k],q=q*pri[k]%mo,sum+=q*(q-1)%mo;
		}
	}
	return sum%mo;
}

int main(){
	scanf("%lld",&n),sqr=sqrt(n),inv6=ksm(6,mo-2);
	prepare();
	printf("%lld",S(n,1)+1);
}

原文地址:https://www.cnblogs.com/DeepThinking/p/13090876.html