Min_25 筛
Min_25 筛 2020.9.24
这阅读量都14k了就离谱。
本来打算把之前的内容删了重写的,纠结了一下还是决定保留下来,大家就当看了图个乐。
Min_25筛是干啥的
求积性函数前缀和。
咋做啊
大致分两步:
- 对所有筛出不超过的所有质数的值之和
- 求解原问题,即所有筛出不超过的所有数的值之和
为了方便,我们定义表示把当成质数计算得到的结果。
定义表示第小的质数,。
(因为一个合数的最小质因子小于其开根号,因此预处理质数时只需要考虑的质数,记表示这部分质数的数量。)
Min_25筛对被求解的积性函数有一定的要求,最重要的一点是当给出时,能够快速的计算出。
第一步
我们记表示以内所有质数以及最小质因子的合数的之和。
初始时。
从小到大枚举质因子,把最小质因子恰好为的那些合数的从中去掉。枚举的所有,需要去掉的部分是并把变成。
最终求出就是以内所有质数的之和。
第二步
我们记表示以内所有质数以及最小质因子的合数的之和。
初始时。
从大到小枚举,把最小质因子恰好为的那些合数的加到中去。枚举的所有,再额外枚举的指数,把加进。
最后就是答案了!!1
时间复杂度
要是写得不对的话可以来喷我。
对于一个数,满足的质数的数量是。
考虑第一步的复杂度。考虑每个被多少个质数枚举到了,有
都是同级的可以提出来(?)
所以总复杂度就是了(迫真)
以下是原来写的。
干什么用的
可以在的时间内求积性函数的前缀和。
别问我为什么是这个复杂度
要求是一个关于的简单多项式,可以快速计算。
怎么做啊
首先我们需要对每个求出。
怎么求呢?
先线性筛出范围内的质数,设表示从小到大第个质数。
设
说人话就是:是质数,或者的最小质因子大于,把内满足条件的加起来就是。
这个东西的实际含义是什么呢?可以参考一下埃氏筛法的运行过程。
假设现在有个数依次排开,第个数是,根据埃氏筛法的那套理论,每次选出一个质数,然后筛掉它的所有倍数。
会发现就是运行次埃氏筛法后,没被筛掉的所有数之和加上所有的。
我们要求的其实就是,其中是质数集合的大小。
考虑的转移,分两种情况:
1、。此时运行的第次已经不会再筛掉任何数了(因为第次运行中筛掉的最小的数是),所以此时。
2、。这时候我们就要考虑哪些数被筛掉了。被筛掉的数一定含有质因子,且除掉后最小的质因子会大于等于。考虑减去,但在中多减去了这些最小质因子小于的函数值,所以再把它们加上就好了。
所以总结起来就是:
关于的初值问题:表示所有数的和,也就是把所有数都当作是质数带入的那个多项式中算出的结果。
因为最后只要求所有的,所以在求的时候数组只开了一维。这样做的复杂度被证明是的。
以即求以内的质数个数为例:
for (int i=1,j;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;
g[m]=(w[m]-1)%mod;
}
for (int j=1;j<=tot;++j)
for (int 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])];
g[i]=(g[i]-g[k]+j-1)%mod;g[i]=(g[i]+mod)%mod;
}
说了那么多你求出了啥?
现在我们已经对于求出了。
我们设,也就是所有满足最小质因子大于等于的值之和。
那么最终的答案就是。
鉴于质数的答案我们已经算出来了,是。(因为要保证最小质因子大于等于所以要把小于它的质数减掉)
考虑合数。我们枚举这个合数的最小质因子及其出现次数,然后直接乘即可。
然后这个的复杂度也被证明是的。
举个栗子
定义积性函数,求其前项和。
会发现除了以外的质数都满足,所以可以分别计算出以及。
在处理的时候,如果,就说明其中包含这个因数,因此把答案即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int N = 1e6+5;
const int mod = 1e9+7;
int Sqr,zhi[N],pri[N],sp[N],tot,m,id1[N],id2[N],g[N],h[N];
ll n,w[N];
void Sieve(int n){
zhi[1]=1;
for (int i=2;i<=n;++i){
if (!zhi[i]) pri[++tot]=i,sp[tot]=(sp[tot-1]+i)%mod;
for (int j=1;i*pri[j]<=n;++j){
zhi[i*pri[j]]=1;
if (i%pri[j]==0) break;
}
}
}
int S(ll x,int y){
if (x<=1||pri[y]>x) return 0;
int k=(x<=Sqr)?id1[x]:id2[n/x];
int res=(1ll*g[k]-h[k]-sp[y-1]+y-1)%mod;res=(res+mod)%mod;
if (y==1) res+=2;
for (int i=y;i<=tot&&1ll*pri[i]*pri[i]<=x;++i){
ll p1=pri[i],p2=1ll*pri[i]*pri[i];
for (int e=1;p2<=x;++e,p1=p2,p2*=pri[i])
(res+=(1ll*S(x/p1,i+1)*(pri[i]^e)%mod+(pri[i]^(e+1)))%mod)%=mod;
}
return res;
}
int main(){
scanf("%lld",&n);
Sqr=sqrt(n);Sieve(Sqr);
for (ll i=1,j;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;
h[m]=(w[m]-1)%mod;
g[m]=((w[m]+2)%mod)*((w[m]-1)%mod)%mod;
if (g[m]&1) g[m]+=mod;g[m]/=2;
}
for (int j=1;j<=tot;++j)
for (int i=1;i<=m&&1ll*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])];
g[i]=(g[i]-1ll*pri[j]*(g[k]-sp[j-1])%mod)%mod;g[i]=(g[i]+mod)%mod;
h[i]=(h[i]-h[k]+j-1)%mod;h[i]=(h[i]+mod)%mod;
}
printf("%d\n",S(n,1)+1);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· 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工具