update 1-17 新增:线性筛约数个数、线性筛约数和html
若一个定义在正整数域上的函数\(f(x)\)对于任意知足\(\gcd(x,y)==1\)的\(x,y\)都有\(f(xy)=f(x)*f(y)\),则\(f(x)\)是积性函数。函数
\(\mu(n)\):莫比乌斯函数
\(\varphi(n)\):欧拉函数
\(d(n)\):一个数\(n\)的约数个数
\(\sigma(n)\):一个数\(n\)的约数和
\(f(x)=x^k(k\in{N})\):这个玩意儿也是积性函数
更多详见百度。spa
若\(f(x),g(x)\)都是积性函数,则它们的狄利克雷卷积\(h(x)=\sum_{d|x}f(d)g(\frac xd)\)也是积性函数。code
能够线性筛。注意是任意积性函数均可以线性筛。htm
一个在严格\(O(n)\)时间复杂度内筛出某个东西的东西。blog
保证每一个数只会被它的最小质因子给筛掉(不一样于埃氏筛中每一个数会被它全部质因子筛一遍从而使复杂度太高)get
int pri[N],tot,zhi[N];//zhi[i]为1的表示不是质数 void sieve() { zhi[1]=1; for (int i=2;i<=n;i++) { if (!zhi[i]) pri[++tot]=i; for (int j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]==0) break; } } }
全部线性筛积性函数都必须基于线性筛素数。class
int mu[N],pri[N],tot,zhi[N]; void sieve() { zhi[1]=mu[1]=1; for (int i=2;i<=n;i++) { if (!zhi[i]) pri[++tot]=i,mu[i]=-1; for (int j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]) mu[i*pri[j]]=-mu[i]; else {mu[i*pri[j]]=0;break;} } } }
int phi[N],pri[N],tot,zhi[N]; void sieve() { zhi[1]=phi[1]=1; for (int i=2;i<=n;i++) { if (!zhi[i]) pri[++tot]=i,phi[i]=i-1; for (int j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]) phi[i*pri[j]]=phi[i]*phi[pri[j]]; else {phi[i*pri[j]]=phi[i]*pri[j];break;} } } }
记\(d(i)\)表示\(i\)的约数个数
\(d(i)=\prod_{i=1}^{k}(a_i+1)\)
维护每个数的最小值因子出现的次数(即\(a_1\))便可百度
int d[N],a[N],pri[N],tot,zhi[N]; void sieve() { zhi[1]=d[1]=1; for (int i=2;i<=n;i++) { if (!zhi[i]) pri[++tot]=i,d[i]=2,a[i]=1; for (int j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]) d[i*pri[j]]=d[i]*d[pri[j]],a[i*pri[j]]=1; else {d[i*pri[j]]=d[i]/(a[i]+1)*(a[i]+2);a[i*pri[j]]=a[i]+1;break;} } } }
记\(\sigma(i)\)表示\(i\)的约数和
\(\sigma(i)=\prod_{i=1}^{k}(\sum_{j=0}^{a_i}p_i^j)\)
维护\(low(i)\)表示\(i\)的最小质因子的指数次幂,即\(p_1^{a_1}\),\(sum(i)\)表示\(i\)的最小质因子对答案的贡献,即\(\sum_{j=0}^{a_1}p_1^j\)
(这玩意儿可能会爆int吧,我这里就无论那么多了)date
int low[N],sum[N],sigma[N],pri[N],tot,zhi[N]; void sieve() { zhi[1]=low[1]=sum[1]=sigma[1]=1; for (int i=2;i<=n;i++) { if (!zhi[i]) low[i]=pri[++tot]=i,sum[i]=sigma[i]=i+1; for (int j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]==0) { low[i*pri[j]]=low[i]*pri[j]; sum[i*pri[j]]=sum[i]+low[i*pri[j]]; sigma[i*pri[j]]=sigma[i]/sum[i]*sum[i*pri[j]]; break; } low[i*pri[j]]=pri[j]; sum[i*pri[j]]=pri[j]+1; sigma[i*pri[j]]=sigma[i]*sigma[pri[j]]; } } }
若想线性筛出积性函数\(f(x)\),就必须可以快速计算出一下函数值:
一、\(f(1)\)
二、\(f(p)\)(\(p\)是质数)
三、\(f(p^k)\)(\(p\)是质数)
其实就是含有的质因子数小于等于1的全部数对应的函数值。
常见的积性函数都会给出上述函数值的有关定义。对于自定义的一个积性函数(如狄利克雷卷积),就须要自行计算出上述函数值。
咱们假设已经完成了上述函数值的计算,如今要求筛出全部至少含有两个质因子的数对应的函数值。
显然,一个含有至少两个质因子的数必定能够被分解成两个互质的且均不为1的数的乘积。此时咱们就能够用\(f(xy)=f(x)f(y)\)计算得出相应的函数值。
如下内容须要彻底理解上面的线性筛素数。
咱们考虑筛的过程当中,\(i*pri_j\)会被\(i\)乘上\(pri_j\)给筛掉。
若将\(i\)惟一分解获得\(p_1^{a_1}p_2^{a_2}...p_k^{a_k}\),则必定有\(pri_j<=p_1\)。
这个不须要证实,由于当\(pri_j=p1\)的时候就break
掉了。
若\(pri_j<p_1\),则\(pri_j\)与\(i\)互质,能够直接\(f(i*pri_j)=f(i)*f(pri_j)\)
若\(pri_j=p_1\),这时就须要对\(i\)记录一个\(low_i\),表示\(i\)中最小值因子的指数次幂,即\(low_i=p_1^{a_1}\)(就是在惟一分解中的那个\(p_1^{a_1}\))。
若是使用\(i\)除掉\(low_i\)那么结果中的最小质因子必定大于\(p_1\),而又由于\(pri_j=p_1\),从而可知\(\gcd(i/low_i,low_i*pri_j)=1\)。那么就能够\(f(i*pri_j)=f(i/low_i)*f(low_i*pri_j)\)
注意当\(low_i=i\)时表示这个数是一个质数的若干次幂,这个时候就须要用到上方的特殊定义。
void sieve() { zhi[1]=low[1]=1; f[1]=对1直接定义; for (ll i=2;i<=n;i++) { if (!zhi[i]) low[i]=pri[++tot]=i,f[i]=对质数直接定义; for (ll j=1;j<=tot&&i*pri[j]<=n;j++) { zhi[i*pri[j]]=1; if (i%pri[j]==0) { low[i*pri[j]]=low[i]*pri[j]; if (low[i]==i) f[i*pri[j]]=对质数的若干次幂进行定义(通常由f[i]递推); else f[i*pri[j]]=f[i/low[i]]*f[low[i]*pri[j]]; break; } low[i*pri[j]]=pri[j]; f[i*pri[j]]=f[i]*f[pri[j]]; } } }
对于某种形如狄利克雷卷积形式的函数\(\sum_{d|x}f(d)g(\frac xd)\),若其中\(f(x)\)或\(g(x)\)不是积性函数,对于数据范围较小(如\(10^6\))的时候能够考虑暴力筛,即枚举一个d去计算能够给哪些x作贡献,复杂度是\(O(\sum_{i=1}^{n}\lfloor\frac ni\rfloor)\)即埃筛的复杂度。若数据范围较大(\(10^7\)埃筛跑不过)就须要去考虑这个函数的一些相关性质了。
实践样例能够参考:
【BZOJ4407】于神之怒增强版
【BZOJ4804】欧拉心算