常用积性函数的线性筛法整理
简单整理推导加代码,留复习用。
线性筛素数
最简单也最基础,直接看代码就好了\(……\)
code:
void Euler_Phi_Prime(int n) {
is_prime[1] = true;
for (int i = 2; i <= n; i++) {
if (!is_prime[i]) prime[++cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
}
线性筛欧拉函数
根据欧拉函数的两条性质\(:\)
-
设\(p\)为素数,若\(p|n\)且\(p^2|n\),则\(\varphi(n)=\varphi(n/p)* p\)
-
设\(p\)为素数,若\(p|n\)且\(p^2 \nshortmid n\),则\(\varphi(n)=\varphi(n/p)* (p-1)\)
对于第一条性质,若\(p|n\)且\(p^2|n\),那么显然\(n\)和\(n/p\)包含相同的质因子,并且将两者按照欧拉函数计算公式展开后只有\(p\)的指数不同,上下相除结果即为\(p\),故此性质成立。
对于第二条性质,若\(p|n\)且\(p^2 \nshortmid n\),说明\(n\)和\(n/p\)互质,根据欧拉函数为积性函数,我们显然可以得到\(\varphi(n)=\varphi(n/p) * \varphi(p)\),又因为\(\varphi(p)=p-1\),所以显然成立。
void Euler_Phi_Prime(int n) {
phi[0] = phi[1] = 0;
for (int i = 2; i <= n; i++) {
if (!phi[i]) prime[++cnt] = i, phi[i] = i - 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
phi[i * prime[j]] = phi[i] * ((i % prime[j]) ? (prime[j] - 1) : prime[j]);
if (i % prime[j] == 0) break;
}
}
}
线性求约数个数
考虑我们根据算数基本定理得到的推论之一:
一个正整数\(N\)的约数个数为:
在筛的过程中,我们记录\(N\)的最小素因子\(st[N]\)
我们根据筛素数分成三种情况考虑:
\(1.\)当前的数\(i\)为素数的时候,显然它只有\(1\)和自己两个因子且只有自己一个素因子,此时\(d(i)=2,\ st[i]=1\)
\(2.\)当\(i\) % \(prime[j]!=0\)时,显然\(i\)不包含\(prime[j]\)这个质因子,且\(prime[j]\)一定是\(i * prime[j]\)的最小质因子,因为我们是在从小到大枚举素数,这样\(i * prime[j]\)就相当于比\(i\)多了一个因子\(prime[j]\),那么\(i * prime[j]\)的约数和就为\(:\)
最后的\((1+1)\)即表示质因数分解后\(prime[j]\)的指数为\(1\),此时\(d(i * prime[j])=d(i) * d(prime[j])\) \(st[i * prime[j]]=1\).
\(3.\)当\(i\)%\(prime[j]==0\)时,显然\(i\)是包含\(prime[j]\)这个质因子的,\(i * prime[j]\)只是比\(i\)多了一个\(i\)的最小质因子,\((\)因为是从小到大枚举素数的\()\)即\(:\)
所以此时\(d(i * prime[j])=d(i) / (st[i] + 1) * (st[i] + 2)\) \(st[i * prime[j]]=st[i]+1\)
code:
void Euler_Phi_Prime(int n) {
is_prime[1] = true, d[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i]) prime[++cnt] = i, d[i] = 2, st[i] = 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = true;
if (i % prime[j] == 0) {
d[i * prime[j]] = d[i] / (st[i] + 1) * (st[i] + 2);
st[i * prime[j]] = st[i] + 1; break;
}
d[i * prime[j]] = d[i] * d[prime[j]], st[i * prime[j]] = 1;
}
}
}
线性筛约数和
考虑算术基本定理的另一个推论\(:\)
一个正整数\(N\)的约数和为\(:\)
筛的过程中我们记录\(st[i]=(1+p_i+p_i^2+ ……+p_i^{c_i})\)
仍旧按筛素数的情况分成三种情况考虑\(:\)
\(1.\)当当前的数\(i\)为质数时,显然\(i\)只有\(1\)和自己两个因子,显然\(sd[i]=st[i]=i+1\)
\(2.\)当\(i\)%\(prime[j]!=0\)时,显然\(i\)并不包含\(prime[j]\)这个质因子,且\(prime[j]\)一定是\(i * prime[j]\)的最小质因子,因为我们是在从小到大枚举素数,这样\(i * prime[j]\)就相当于比\(i\)多了一个因子\(prime[j]\),答案多累加上\(prime[j]\)的贡献即可\(:\)
此时,\(sd[i* prime[j]]=sd[i] * sd[prime[j]],\ st[i* prime[j]]=prime[j]+1\)
\(3.\)当\(i\)%\(prime[j]==0\)时,显然\(i\)是包含\(prime[j]\)这个质因子的,\(i * prime[j]\)只是比\(i\)多了一个\(i\)的最小质因子,\((\)因为是从小到大枚举素数的\()\),所以\(i* prime[j]\)的约数和就表示为\(:\)
此时,\(sd[i * prime[j]]=sd[i] / st[i] * (st[i] * prime[j]+1),\ st[i * prime[j]]=st[i]* prime[j]+1\)
code:
void Euler_Phi_Prime(int n) {
is_prime[1] = true, sd[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i]) prime[++cnt] = i, sd[i] = i + 1, st[i] = i + 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = true;
if (i % prime[j] == 0) {
sd[i * prime[j]] = sd[i] / st[i] * (st[i] * prime[j] + 1);
st[i * prime[j]] = st[i] * prime[j] + 1; break;
}
sd[i * prime[j]] = sd[i] * sd[prime[j]], st[i * prime[j]] = prime[j] + 1;
}
}
}
线性筛莫比乌斯函数
窝太菜了,证明还是先鸽着吧\(……\)
code:
void Euler_phi(int n) {
is_prime[1] = true, mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i])
prime[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && (i * prime[j]) <= n; j++) {
is_prime[i * prime[j]] = true;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
}