Min25筛法学习小记
问题引入#
已知积性函数
要求其前项和()
其中对于所有质数
满足
也就是可以表示成一个低阶多项式
且可以快速计算
I#
定义表示的最小质因子,表示第个质数,特别的,
我们把答案拆成质数,合数,与1
也就是
1的贡献是1
质数的贡献:
合数的贡献
我们可以枚举合数的最小质因子和质因子的指数
内层换成枚举的倍数,则等价于
注意因为枚举倍数,所以都会有,因为是积性函数,所以可以提出来
但是还有问题
因为本身也有贡献,如果的话
因为也就是质数,我们已经贡献了
最终的式子是
中间一坨看着很不爽
我们设
那么答案就是
考虑用这个式子代替上式
考虑如何推
和上面一样,考虑质数和合数的贡献
质数的贡献
合数的贡献
总结一下就是
因为我们最初给出了限制:可以快速计算,所以这个可以忽略
注意到以为的最小合数是
所以后边的只需枚举到,总体的这个也是只到
因此后半部分只需要筛出来小于等于的质数就可以递归进行了
问题在于前半部分
II#
我们现在要求
根据上边可以知道这里的是小于等于的
我们可以预处理出表示前个质数的之和
那么上式等价于
也就是我们只要能知道所有质数的函数就行了
又因为我们一开始限制,函数在质数处的取值是低阶多项式
所以我们拆成一些单项式,分别求和就行了
也就是说我们只需对形如在质数处求和就行了
注意到这是一个完全积性函数哦
与类似,我们设
通俗来说就是用前个质数做线性筛剩下的数的函数之和
那么所有质数的之和,就是
其中是满足的最大,也就是我们筛出来的最后一个质数
考虑怎么递推
它肯定是在的基础上多筛出了一下数
那么筛出了那些数呢,就是以的
我们依旧可以提出一个
直接枚举倍数
注意,此时后面可能还有,但是因为是完全积性函数,所以没事
似乎后面的东西和我们的有点像,观察其实就是少算了那些的质数
那么直接用减掉就行了
以为阶段,递推式就出来了
不过我们似乎存不下这么多值
观察到因为
所以最后一项可以预处理
问题就在于
但是我们又发现
也就是说,我们访问到的只会是形如
这样子的位置的值
根据整除分块的理论,这种取值只会有个
就开的下了
不过因为每个数都很大
所以要么可以离散化,要么可以用这种方法
设为某一个可能的取值的位置
若,直接记录
否则,记录
根据某些证明,该算法的时间复杂度是
但是实际的效率是很高的
III#
应用
P5325 【模板】Min_25筛#
直接把在质数处的表达式告诉我们了
我们直接拆成,和
用就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 1e6+7;
const LL mod = 1e9+7;
const LL inv2=5e8+4,inv3=333333336;
LL prime[N],tot=0;
LL sum[N],Sum[N];
LL Id1[N],Id2[N];
LL v[N];
void init(LL n)
{
v[1]=0;
for(LL i=2;i<=n;i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
sum[tot]=(sum[tot-1]+i)%mod;
Sum[tot]=(Sum[tot-1]+i*i%mod)%mod;
}
for(LL j=1;j<=tot;j++)
{
if(i*prime[j]>n||prime[j]>v[i]) continue;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
LL B;
LL num[N],cnt=0;
LL G1[N],G2[N];
LL W=0;
LL S(LL n,LL m)
{
if(prime[m]>=n) return 0;
LL id=(n<=B?Id1[n]:Id2[W/n]);
LL ans=(G2[id]-G1[id]+mod)%mod-(Sum[m]-sum[m]+mod)%mod;
ans=(ans+mod)%mod;
// cout<<n<<' '<<m<<' '<<id<<' '<<G2[id]-G1[id]<<endl;
for(LL k=m+1;k<=tot&&prime[k]*prime[k]<=n;k++)
{
LL cur=prime[k];
for(LL c=1;cur<=n;c++,cur=cur*prime[k])
{
LL x=cur%mod;
LL v=x*(x-1)%mod;
ans=(ans+v*(S(n/cur,k)+(c!=1)%mod)%mod)%mod;
}
}
return ans;
}
int main()
{
LL n;
cin>>n;
B=sqrt(n);
init(B);
W=n;
LL l=1,r;
// cout<<3*inv3%mod;
for(;l<=n;l=r+1)
{
r=(n/(n/l));
num[++cnt]=(n/l);
LL tmp=num[cnt]%mod;
G1[cnt]=tmp*(tmp+1)%mod*inv2%mod-1;
G2[cnt]=tmp*(tmp+1)%mod*inv2%mod*(2*tmp%mod+1)%mod*inv3%mod-1;
if(n/l<=B) Id1[n/l]=cnt;
else Id2[n/(n/l)]=cnt;
}
for(LL i=1;i<=tot;i++)
{
for(LL j=1;j<=cnt&&prime[i]*prime[i]<=num[j];j++)
{
LL v=num[j]/prime[i],id;
if(v<=B) id=Id1[v];
else id=Id2[n/v];
// cout<<j<<' '<<id<<' '<<G2[id]<<endl;
G1[j]-=prime[i]*(G1[id]-sum[i-1]+mod)%mod;
G2[j]-=prime[i]*prime[i]%mod*(G2[id]-Sum[i-1]+mod)%mod;
G1[j]%=mod;
G2[j]%=mod;
if(G1[j]<0) G1[j]+=mod;
if(G2[j]<0) G2[j]+=mod;
}
}
cout<<(S(n,0)+1)%mod;
return 0;
}
同样也可解决许多杜教筛可以解决的问题
筛#
直接算就行了
筛#
直接算就行了
筛除数函数#
应该也可以筛一些奇奇怪怪的东西
比如
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】