常见积性函数线性筛
线性筛素数
比埃氏筛更优的线性筛,线性体现在每个数会被它最小的质因子筛掉,最小的质因子显然只有一个,每个数只会被访问一次,严格的线性复杂度。
一个数被访问是由枚举“除去它最小的质因子后的值 \(i\) ”,再乘上“最小质因子 \(prime[j]\)(以下简写为\(p[j]\))”实现的。
例如 \(8\) 是在 \(i=4,p[j]=2\) 时被访问的,\(12\) 是在 \(i=6,p[j]=2\) 时被访问的,\(9\) 是 \(i=3,p[j]=3\) (可以看到需要先把 \(i\) 加入质数中再枚举质数)。
若当前的 \(i\) 没有被访问,说明 \(i\) 最小的质因子是它本身,显然 \(i\) 就是个质数。
为了保证当前质因子是乘积的最小质因子,当 \(i\%p[j]==0\) 时停止对 \(j\) 的枚举,因为此时 \(i\) 中保证有质因子 \(p[j]\),当 \(p[j]\) 继续增大时,\(i\) 与 \(p[j]\) 乘积的最小质因子不会再是 \(p[j]\),而是 \(i\) 中的最小质因子。
for(int i=2; i<=n; i++) {
if(!vis[i]) p[++cnt] = i;
for(int j=1; j<=cnt && i*p[j]<=n; j++) {
vis[ i*p[j] ] = 1;
if(i%p[j]==0) break; //与埃氏筛唯一的区别
}
}
积性函数
积性函数是指当 \(a\) 与 \(b\) 互质时满足 \(f(ab)=f(a)*f(b)\) 的函数。常见的像欧拉函数 \(\varphi\) ,约数个数 \(d\), 约数和 \(\sigma\),莫比乌斯函数 \(\mu\)。所有的积性函数都能线筛(大概)。
我们在线性筛时,每个数是由 \(i\) 与 \(p[j]\) 乘起来得到的。
若 \(i\%p[j]\ne 0\),因为 \(p[j]\) 是质数所以 \(i\) 与 \(p[j]\) 互质,\(f( i*p[j] )=f(i)*f(p[j])\)。
若 \(i\%p[j]=0\),我们一般需要考虑这个数中最小的质因子 \(p[j]\) 的出现次数对它函数值的影响,一般需要另设一个函数 \(g\) 来记录最小质因子的出现次数,当 \(i\%p[j]=0\) 时,\(g[i*p[j]]=g[i]+1\),而 \(f\) 值就要具体情况具体分析了。
欧拉函数 \(\varphi\) 线筛
欧拉函数 \(\varphi\),表示比 \(x\) 小的与 \(x\) 互质的数的个数,求单个的话是 \(sqrt\) 复杂度的 (如果忽略预处理的复杂度就是 \(sqrt / log\))。
若\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),则\(\varphi(x)=x\prod\limits_{i=1}^n\dfrac {p_i-1} {p_i}\)。
这个很好证,就是考虑每个质因子对 \(\varphi\) 值的贡献。
\(\varphi\) 是积性函数也就显然了,因为互质的两个数没有相同的 \(p\),两个数的质因子贡献互不影响。
若 \(i\%p[j]\ne 0\) ,\(\varphi(i*p[j])=\varphi(i)*\varphi(j)=\varphi(i)*(p[j]-1)\);
若 \(i\%p[j]=0\) ,由于指数 \(k_j\) 不会对答案造成影响,而 \(p[j]\) 已经对 \(i\) 产生过贡献,所以直接乘上 \(p[j]\) 即可,\(\varphi(i*p[j])=\varphi(i)*p[j]\)。
for(int i=2; i<=n; i++) {
if(!vis[i]) {
p[++cnt] = i;
phi[i] = i-1;
}
for(int j=1; j<=cnt && i*p[j]<=n; j++) {
vis[ i*p[j] ] = 1;
if(i%p[j]==0) {
phi[ i*p[j] ] = phi[i] * p[j];
break;
}
phi[ i*p[j] ] = phi[i] * (p[j]-1);
}
}
约数个数 \(d\) 线筛
一个显然的结论:若\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),则\(d(x)=\prod\limits_{i=1}^n(k_i+1)\)。
若 \(x\) 与 \(y\) 互质,那么 \(x\) 与 \(y\) 没有相同的质因子,显然\(d(xy)=d(x)*d(y)\),所以 \(d\) 是个积性函数,考虑同样的线筛方式。
若 \(i\%p[j]\ne 0\) ,\(d(i*p[j])=d(i)*d(p[j])=d(i)*2\);
若 \(i\%p[j]=0\) ,相当于 \(p[j]\) 的指数从 \(k_j\) 变成了 \(k_j+1\),辣么它的d就从一大坨乘上 \(k_j+1\) 变成一大坨乘上 \(k_j+2\) 。
所以有必要开一个 \(g\) 记录最小质因子出现次数,\(g[ i*p[j] ]=g[i]+1,d[i*p[j]]=d[i]/(g[i]+1)*(g[i]+2)\)。记得当 \(i\) 是质数时,\(d[i]=2,g[i]=1\)。
d[1] = 1;
for(int i=2; i<=n; i++) {
if(!vis[i]) {
p[++cnt] = i;
d[i] = 2; g[i] = 1;
}
for(int j=1; j<=cnt && i*p[j]<=n; j++) {
vis[ i*p[j] ] = 1;
if(i%p[j]==0) {
d[ i*p[j] ] = d[i] / (g[i]+1) * (g[i]+2);
g[ i*p[j] ] = g[i] + 1;
break;
}
d[ i*p[j] ] = d[i] * 2;
g[ i*p[j] ] = 1;
}
}
约数和 \(\sigma\) 线筛
若\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),那么有一个结论是
\(\sigma(x)=(1+p_1+{p_1}^2+...+{p_1}^{k_1})*(1+p_2+{p_2}^2+...+{p_2}^{k_2})*...*(1+p_n+{p_n}^2+...+{p_n}^{k_n})\)。
简写就是 \(\sigma(x)=\prod\limits_{i=1}^n\sum\limits_{j=0}^{k_i}{p_i}^j\)。
因为从每个括号中各选一项相乘得到的数一定是 \(x\) 的约数,而且是一一对应的,将所有项加起来就是约数和了。
由于互质的两个数质因子互不相同,联乘可以叠在一起,所以 \(\sigma\) 是积性函数。
若 \(i\%p[j]\ne 0\), \(\sigma(i*p[j])=\sigma(i)*\sigma(p[j])=\sigma(i)*(p[j]+1)\)。
若 \(i\%p[j]=0\), 相当于将 $p_j $ 的指数 \(k_j\) 加一,则 \(p_j\) 对答案的贡献从 \((1+p_j+{p_j}^2+...+{p_j}^{k_j})\) 变为 \((1+p_j+{p_j}^2+...+{p_j}^{k_j}+{p_j}^{k_j+1})\),而不会对其他质因数造成影响。
设 \(g[i]\) 表示 \(i\) 中不能被 \(p_j\)(\(i\) 的最小质因子)整除的约数的和,
即\(\sigma(i)=(1+p_j+{p_j}^2+...+{p_j}^{k_j})*g[i]\)
则 \(\sigma(i*p[j])=(1+p_j+{p_j}^2+...+{p_j}^{k_j})*p_j*g[i]+g[i]\)
\(=(1+p_j+{p_j}^2+...+{p_j}^{k_j}+{p_j}^{k_j+1})*g[i]=\sigma(i)*p[j]+g[i]\)
然后注意 \(g\) 数组的赋值:当 \(p[j]\) 是 \(i\) 的质因子时,\(g[i*p[j]]=g[i]\),否则 \(g[i*p[j]]=f[i]\),这个应该好理解。
代码中 \(\sigma\) 用 \(f\) 表示:
f[1] = 1;
for(int i=2; i<=n; i++) {
if(!vis[i]) {
p[++cnt] = i;
f[i] = i+1; g[i] = 1;
}
for(int j=1; j<=cnt && i*p[j]<=n; j++) {
vis[ i*p[j] ] = 1;
if(i%p[j]==0) {
f[ i*p[j] ] = f[i] * p[j] + g[i];
g[ i*p[j] ] = g[i];
break;
}
f[ i*p[j] ] = f[i] * (p[j]+1);
g[ i*p[j] ] = f[i];
}
}
2020.06.06 update:
这玩意今天模拟赛出了个应用。。谔谔。
大概是问你 \(\sum\limits_{i=1}^n n\%i\),这玩意儿显然可以用整除分块来根号复杂度做,但是这题多组询问的 \(T\) 达到了 \(1e6\),那么就找规律。
发现这个数列:\(\sum\limits_{i=1}^n \left\lfloor\frac ni\right\rfloor i\), \(1,4,8,15,21...\)
和刚刚筛出来的 \(\sigma:\) , \(1,3,4,7,6...\)
为啥是这个前缀和呢? \(@159\) 给出了理性的证明:\(\left\lfloor\frac ni\right\rfloor i\) 可以等价于小于等于 \(n\) 的数中有多少个数是 \(i\) 的倍数,再乘个
\(i\),把所有 \(i\) 的倍数加起来,就是把所有数的因数和加起来。
莫比乌斯函数 \(\mu\) 线筛
想做反演题你得先把它筛出来。
莫比乌斯函数是 人类自己捏出来的 一个容斥系数函数,来个通俗一点的定义:
若x=1,则\(\mu(x)=1\);当 \(x>1\) 时分解 \(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\);
若 \(k_1=k_2=k_3=...=k_n=1\),\(\mu(x)=(-1)^n\);
否则\(\mu(x)=0\)。
\(\mu\) 显然是个积性函数,感性证明:
若 \(x\) 与 \(y\) 互质,若 \(\mu(x)=0\) 或 \(\mu(y)=0\),说明有一个质因子的指数大于1,那么xy那个质因子指数也大于1,\(\mu(xy)=\mu(x)*\mu(y)=0\);
当 \(x\ne 0\) 且 \(y\ne 0\) 时,\(\mu(x)=(-1)^{n_x},\mu(y)=(-1)^{n_y},\mu(xy)=(-1)^{n_x+n_y}=\mu(x)*\mu(y)\)。
考虑线筛:
若 \(i\%p[j]\ne0\),\(\mu(i*p[j])=\mu(i)*\mu(p[j])=-\mu(i)\),因为质数的 \(\mu\) 都是 \(-1\);
若 \(i\%p[j]=0\),说明 \(p[j]\) 这个质因子将在 \(i*p[j]\) 出现两次,那\(\mu(i*p[j])\) 就等于 \(0\) 呗。
mu[1] = 1;
for(int i=2; i<=n; i++) {
if(!vis[i]) {
p[++cnt] = i;
mu[i] = -1;
}
for(int j=1; j<=cnt && i*p[j]<=n; j++) {
vis[ i*p[j] ] = 1;
if(i%p[j]==0) break; //mu[i*p[j]]=0;就没必要写了。
mu[ i*p[j] ] = -mu[i];
}
}
参考:
https://www.cnblogs.com/cjoieryl/p/8268373.html