线性筛--如何线性求约数个数
线性筛1654948748
我已经掌握埃氏筛了 为什么还要学线性筛???
线性筛的时间复杂度是严格 O(N)O(N) 的, 而埃氏筛的复杂度是 O(N∗log2(log2(N))O(N∗log2(log2(N))
看上去并没有快多少 实际也是, 但在处理一些大数据时,差距就凸显出来了
算法思路
概述:
和埃氏筛类似的
线性筛是通过枚举到的当前数字乘以某个比该数的最小质因子还小的质数来筛的
(上面这句话有点长, 需要反复理解 暂时不能理解,也没关系,等下代码中我会详细讲的)
这样就可以保证每次几乎不会出现重筛的情况,大大减少了循环次数
代码:
#include<bits/stdc++.h> using namespace std; #define re register #define ll long long #define in inline #define get getchar() int read() { int t=0; char ch=get; while(ch<'0' || ch>'9') ch=get; while(ch<='9' && ch>='0') t=t*10+ch-'0',ch=get; return t; } const int _=1e7+6; int prime[_], tot, n; //tot是素数个数,prime是从小到大存放的素数数组 bool flag[_];//用来判断当前数已为素数,flag==1不是素数 int main() { n=read(); for (re int i=2;i<=n;i++) { if (!flag[i]) prime[++tot]=i; // 当前数没有被打上非素数标记 for (re int j=1;j<=tot&&prime[j]*i<=n;j++) { //保证prime[j]在已确定的素数范围内 flag[prime[j]*i]=1; //打上非素数标记 if(i%prime[j]==0) break; //若当前prime[j]已是i的约数 //剩下的不用重复处理,所以直接break } } cout<<"TOT: "<<tot<<endl; for (re int i=1;i<=tot;i++) cout<<prime[i]<<' '; return 0; }
用线性筛在线性时间里求1~n的约数个数
思路
记录数字 i 的最小约数出现次数 minn[i]
然后通过我们以前的知识 小学奥数 知道d(i)==(每个质因数的指数+1)的积
然后利用线性筛的性质递推 minn[i] 与 d[i] 就好
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define get getchar() #define in inline #define re register const int _=10000001; int minn[_],n,tot,prime[_],d[_]; bool np[_]; int main() { cin>>n; d[1]=1; for(re int i=2; i<=n; i++) { if(np[i]==0) prime[++tot]=i,d[i]=2,minn[i]=1; for(re int j=1;prime[j]*i<=n&&j<=tot;j++) { np[i*prime[j]]=1; if(i%prime[j]==0) { minn[i*prime[j]]=minn[i]+1; d[i*prime[j]]=d[i]/(minn[i]+1)*(minn[prime[j]*i]+1); break; } minn[i*prime[j]]=1; d[i*prime[j]]=d[i]*2; } } for( re int i=1;i<=n;i++) cout<<d[i]<<' '; }
嗯,就这样了...