Min_25筛学习笔记

Min_25筛学习笔记

这种神仙东西不写点东西一下就忘了QAQ

资料和代码出处
资料2
资料3
打死我也不承认参考了yyb的

Min_25筛可以干嘛?下文中未特殊说明P均指质数集合,pip指某个具体质数。

求一类积性函数f(x)的前缀和,需要满足f(p)可以写成多项式的形式,或者操作一下可以写成多项式(如例题),且f(pk)能快速求出。

讲真学这个东西比我什么都不会的时候学FFT都累。

Round 1

先求质数的贡献。我们要求

i=1x[i]f(i)$$$$g(n,j)=i=1n[iP or min(p)>Pj]f(i)

注意到当pj2>x时,g(n,j)=g(n,j1)因为没有新的贡献了,所以我们只要筛到n的质数就可以了,这也是降低复杂度的关键。

我们还注意到g(n,|P|)=i=1x[i]f(i)|P|n内的质数集合大小。

关于g(n,j)的转移,我们有:

g(n,j)=g(n,j1)f(Pj)[g(nPj,j1)i=1j1f(Pi)],Pj2n

意思大概就是减掉所有最小质因子为pj的贡献,但由于g(npj,j1)里包含了质数,而<pj的质数是不能算的,所以要减掉。

由于我们只需要g(n,|P|),所以下面的代码是一维的,用递推实现。注意到过程中我们只需要g(ni,|P|),所以最多只有2n种取值

至于实现,由于我参考了gsy的实现,痛不欲生,于是决定把他的代码蒯走。这份代码是筛f(p)=1的。

//这两个鬼id就是你在杜教筛中碰到的卡常卡空间技巧,这份代码你理解了这个就能看懂
//至于YL,是机房众人mo的巨佬,所以是模数
//Sq是根号n
for (int i=1,j;i<=n;i=j+1)
{
    j=n/(n/i);w[++m]=n/i;g[m]=(w[m]-1)%YL;//除法分块,根号n求出所有有用的值
    if(w[m]<=sq)id1[w[m]]=m;else id2[n/w[m]]=m;
}
for (int j=1;j<=tot;++j)
    for (int i=1;i<=m&&pri[j]*pri[j]<=w[i];++i)//i再往上就是所有的质数,会被后面抵消
    {
        int k=(w[i]/pri[j]<=sq)?id1[w[i]/pri[j]]:id2[n/(w[i]/pri[j])];
        g[i]=(g[i]-g[k]+(j-1))%YL;g[i]=(g[i]+YL)%YL;
    }

Round 2

然而敌人并没有这么容易就被打倒,我们还有合数没算呢。那么我们鼓捣一个S(n,i)出来

S(n,j)=i=1n[min(p)Pj]f(i)

注意S(n,1)没算到f(1)。答案就是S(n,1)+f(1)

递推式来了

S(n,j)=()+()

有没有感觉被骗了QwQ,我们继续

()=g(n,|P|)i=1j1f(Pi)

()=k=jPk2ne=1Pke+1nS(nPke,k+1)×f(Pke)+f(Pke+1)

经过YL的指点,我可以口胡一下了,每个合数要在最小质因子处被筛到。当npke<pk+1时,肯定是0就没必要继续了。

举个栗子,形如tpk3(其中t的最小质因子大于pk)的数会被S(npk3,4)包含。

由于S这个函数在任何时候都不包含f(1)所以我们要手动加上f(pk2),f(pk3),...,f(pke+1)其中pke+2>n。什么,你问我f(pk)去哪了,这不是个质数么。

完结撒花

放上LOJ6053简单的函数作为例题,这里面的p^c对于质数来说除了2以外都是p1,像个多项式。

代码中g(x,|P|)=i=1x[i]ih(x,|P|)=i=1x[i]1

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
inline ll in()
{
	ll k=0;char ch=gt;
	while(ch<'-')ch=gt;
	while(ch>'-')k=k*10+ch-'0',ch=gt;
	return k;
}
const int N=1e6+5,YL=1e9+7,mod=YL;
inline int MO(const int &x){return x>=YL?x-YL:x;}
ll n,w[N];
int np[N],pr[N],tot,h[N],g[N],sp[N],id1[N],id2[N],m,sq;
void seive(int n)
{
	for(int i=2;i<=n;++i)
	{
		if(!np[i])pr[++tot]=i,sp[tot]=MO(sp[tot-1]+i);
		for(int j=1;i*pr[j]<=n;++j)
		{np[i*pr[j]]=1;if(i%pr[j]==0)break;}
	}
}

int S(ll x,int y)
{
	if(x<=1||pr[y]>x)return 0;
	int k=(x<=sq?id1[x]:id2[n/x]);
	int res=MO(((ll)g[k]-h[k]-sp[y-1]+y-1)%YL+YL);
	if(y==1)res+=2;
	for(int i=y;i<=tot&&1ll*pr[i]*pr[i]<=x;++i)
	{
		ll p1=pr[i],p2=p1*pr[i];
		for(int e=1;p2<=x;++e,p1=p2,p2*=pr[i])
			res=MO(res+(1ll*S(x/p1,i+1)*(pr[i]^e)+(pr[i]^e+1))%YL);
	}
	return res;
}

int main()
{
	n=in();sq=sqrt(n);seive(sq);ll t;
	for(ll i=1,j;i<=n;i=j+1)
	{
		j=n/(n/i),w[++m]=n/i;
		if(w[m]<=sq)id1[w[m]]=m;else id2[n/w[m]]=m;
		h[m]=(w[m]-1)%YL;g[m]=((w[m]+2)%YL)*((w[m]-1)%YL)%YL;
		if(g[m]&1)g[m]+=YL;g[m]>>=1;
	}
	
	for(int j=1;j<=tot;++j)
		for(int i=1;i<=m&&1ll*pr[j]*pr[j]<=w[i];++i)
		{
			t=w[i]/pr[j];int k=(t<=sq?id1[t]:id2[n/t]);
			h[i]=MO((h[i]-h[k]+j-1)%YL+YL);
			g[i]=MO(MO(g[i]-1ll*pr[j]*(g[k]-sp[j-1])%YL)+YL);
		}
	printf("%d\n",S(n,1)+1);
	return 0;
}

一些也许是高阶的应用?

筛与最小质因子有关的东西,用第一步筛,筛的时候顺便处理一下。

筛最大次大质因子有关,考虑后面的筛,其中在乱搞一下就行了。

主要要深刻的理解min_25筛的过程,本质上是容斥?(我口胡的

多做题就明白了。

我偷偷的把YCB的题单蒯过来。

泥萌看着办吧。

posted @   Cgod  阅读(327)  评论(4编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示