机房里差不多都会Min_25筛了,我也赶紧补一波坑v_v
参考:
txc的Min_25筛学习笔记
yx的Min_25筛学习笔记
由于前面两位dalao的标题都是笔记,所以我这里就是小记了,因为这里只记录我的一些思考,并不全面
我也看了2018的论文集,那里的说明比较规范
我听yx说只要是多项式都能用Min_25筛前缀和
实际上,存在一种实现难度更低,同时在较小的数据范围下表现更好的方法,它和洲阁筛的思想类似,但理解起来更为容易
——朱震霆, 《一些特殊的数论函数求和问题》, 国家集训队2018论文集。
听说能代替洲阁筛,所以还不会洲阁筛的我赶紧来学一波操作
在不考虑常数,只考虑复杂度的情况下,Min_25筛是比杜教筛快的,Min_25的复杂度是 Θ ( n 3 4 l o g n ) \Theta(\frac{n^{\frac34}}{log\ n }) Θ(log nn43)的,而杜教筛的复杂度是 Θ ( n 2 3 ) \Theta(n^{\frac23}) Θ(n32),使用Mathematica计算可以发现,当 n n n的值满足 2.08977 < n < 3.00405 ∗ 1 0 22 2.08977<n<3.00405*10^{22} 2.08977<n<3.00405∗1022的时候,Min_25筛的复杂度是更优的,下界…不用管,上界的话如果 n n n到了这个级别按这两个复杂度已经做不了了(别想着打表,跑出这样的数据可能要跑100+天,再说空间复杂度也受不了),所以Min_25筛的效率还是非常优越的
Min_25筛空间复杂度 Θ ( n ) \Theta(\sqrt n) Θ(n)
再考虑适用范围:
杜教筛限制重重,需要更麻烦的推式子,而看Min_25适用的范围的介绍:
如果一个积性函数 f(i)在 i是质数时是一个关于 i的低次多项式,并且在质数的幂处的能快速求,那么大概可以用Min_25筛来求
——txc
相比之下,Min_25筛无疑是适用范围较广的
(yx:现在又找到杜教筛能做的而Min_25筛不能做的题吗,如果有欢迎评论)
咕咕咕咕咕
咕咕咕咕咕
等博主学完Min_25筛后,发现别人已经写的比较清楚了,我会在后面给出一些补充,在这些补充下,就能学完Min_25筛
博主觉得自己在写一遍没有什么意义
#include
#include
#include
#include
#include
namespace fast_IO
{
const int IN_LEN=10000000,OUT_LEN=10000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
LL sum1[700001];
int T,n,part,tot,qz[700001],prime[700001],sq[700001],primesize,sum0[700001],id[700001];
inline int ck(const int x){return x<=part?x:tot-n/x+1;}
int calc_mu(const int a,const int b)
{
if(a<prime[b])return 0;
int ans=sum0[ck(a)]+b-1;
for(rg int i=b;i<=primesize&&sq[i]<=a;i++)ans-=calc_mu(a/prime[i],i+1);
return ans;
}
LL calc_phi(const int a,const int b)
{
if(a<prime[b])return 0;
LL ans=sum1[ck(a)]-(qz[b-1]-(b-1));
for(rg int i=b;i<=primesize&&sq[i]<=a;i++)
{
ans+=(calc_phi(a/prime[i], i+1)+prime[i])*(prime[i]-1);
for(rg int x=prime[i]*prime[i],mine=x-prime[i];(LL)x*prime[i]<=a;x*=prime[i],mine*=prime[i])ans+=(calc_phi(a/x, i+1)+prime[i])*mine;
}
return ans;
}
int main()
{
read(T);
while(T--)
{
read(n);
if(!n){print(0),putchar(' '),print(0),putchar('\n');continue;}
part=sqrt(n);
tot=primesize=0;
for(rg int i=1;i<=part;i++)id[++tot]=i,sum0[tot]=i-1,sum1[tot]=((((LL)i+1)*i)>>1)-1;
id[++tot]=n/part;if(id[tot]!=part)sum0[tot]=id[tot]-1,sum1[tot]=((LL)(((LL)id[tot]+1)*id[tot])>>1)-1;else tot--;
for(rg int i=part-1;i>=1;i--)id[++tot]=n/i,sum0[tot]=id[tot]-1,sum1[tot]=((((LL)id[tot]+1)*id[tot])>>1)-1;
for(rg int i=2;i<=part;i++)
if(sum0[i]!=sum0[i-1])
{
const int limit=(LL)i*i;
for(rg int j=tot;id[j]>=limit;j--)
{
const int t=ck(id[j]/i);
sum1[j]-=(sum1[t]-qz[primesize])*i;
sum0[j]-=sum0[t]-primesize;
}
prime[++primesize]=i,sq[primesize]=i*i,qz[primesize]=qz[primesize-1]+i;
}
for(rg int i=1;i<=tot;i++)sum1[i]-=sum0[i],sum0[i]=-sum0[i];//sum1为phi的前缀和,sum0为mu的前缀和
print(calc_phi(n,1)+1),putchar(' '),print(calc_mu(n,1)+1),putchar('\n');
}
return flush(),0;
}
Min_25筛本身代码并没有很长,我认为关键在于写对(因为对于不同的函数写法不一样)。如果熟练掌握了Min_25筛,还是能简化不少问题的