资料和代码出处
资料2
资料3
打死我也不认可参考了yyb的html
\(Min\_25\)筛能够干吗?下文中未特殊说明\(P\)均指质数集合,\(p_i\)或\(p\)指某个具体质数。函数
求一类积性函数\(f(x)\)的前缀和,须要知足\(f(p)\)能够写成多项式的形式,或者操做一下能够写成多项式(如例题),且\(f(p^k)\)能快速求出。学习
讲真学这个东西比我什么都不会的时候学\(FFT\)都累。spa
先求质数的贡献。咱们要求code
\[\sum_{i=1}^x[i是质数]f(i)\]。htm
咱们能够设blog
\[g(n,j)=\sum_{i=1}^{n}[i \in P \ or\ \min(p)>P_j]f(i)\]get
注意到当\(p_j^2>x\)时,\(g(n,j)=g(n,j-1)\)由于没有新的贡献了,因此咱们只要筛到\(\sqrt n\)的质数就能够了,这也是下降复杂度的关键。string
咱们还注意到\(g(n,|P|)=\sum_{i=1}^x[i是质数]f(i)\),\(|P|\)是\(\sqrt n\)内的质数集合大小。it
关于\(g(n,j)\)的转移,咱们有:
\[g(n,j)=g(n,j-1)-f(P_j)[g(\frac{n}{P_j},j-1)-\sum_{i=1}^{j-1}f(P_i)],P_j^2\le n\]
意思大概就是减掉全部最小质因子为\(p_j\)的贡献,但因为\(g(\frac n{p_j},j-1)\)里包含了质数,而\(<p_j\)的质数是不能算的,因此要减掉。
因为咱们只须要\(g(n,|P|)\),因此下面的代码是一维的,用递推实现。注意到过程当中咱们只须要\(g(\lfloor\frac ni\rfloor,|P|)\),因此最多只有\(2\sqrt n\)种取值
至于实现,因为我参考了\(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; }
然而敌人并无这么容易就被打倒,咱们还有合数没算呢。那么咱们鼓捣一个\(S(n,i)\)出来
\[S(n,j)=\sum_{i=1}^n[\min(p)\ge P_j]f(i)\]
注意\(S(n,1)\)没算到\(f(1)\)。答案就是\(S(n,1)+f(1)\)。
递推式来了
\[S(n,j)=(质数的贡献)+(合数的贡献)\]
有没有感受被骗了\(QwQ\),咱们继续
\[(质数的贡献)=g(n,|P|)-\sum_{i=1}^{j-1}f(P_i)\]
\[(合数的贡献)=\sum_{k=j}^{P_k^2\le n}\sum_{e=1}^{P_k^{e+1}\le n}S(\frac{n}{P_k^e},k+1)\times f(P_k^e)+f(P_k^{e+1})\]
通过\(YL\)的指点,我能够口胡一下了,每一个合数要在最小质因子处被筛到。当\(\frac n{p_k^e}<p_{k+1}\)时,确定是\(0\)就不必继续了。
举个栗子,形如\(t*p_k^3\)(其中\(t\)的最小质因子大于\(p_k\))的数会被\(S(\frac n{p_k^3},4)\)包含。
因为\(S\)这个函数在任什么时候候都不包含\(f(1)\)因此咱们要手动加上\(f(p_k^2),f(p_k^3),...,f(p_k^{e+1})\)其中\(p_k^{e+2}>n\)。什么,你问我\(f(p_k)\)去哪了,这不是个质数么。
放上LOJ6053简单的函数做为例题,这里面的\(p\)^\(c\)对于质数来讲除了\(2\)之外都是\(p-1\),像个多项式。
代码中\(g(x,|P|)=\sum_{i=1}^x[i是质数]i\),\(h(x,|P|)=\sum_{i=1}^x[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的题单蒯过来。
泥萌看着办吧。