【数学】Euler函数
Euler函数的定义:小于等于n的与n互质的正整数的个数,或者小于n的与n互质的自然数的个数。这两个结果都是指向 \(gcd(0,1)=1\) 也就是说 \(\varphi(1)=1\) 。
\(\varphi(p)=p-1\)
\(\varphi(p^k)=p^k-p^{k-1}=p^{k-1}(p-1)\) 就是 \(p^k\) 里面去除掉所有的 \(p\) 的倍数。
合数的情况使用积性函数的性质求出,或者用 \(\varphi(n)=n\prod\limits_{t=1} (1-\frac{1}{p_t})\) 也就是n乘以1-每种质数的倒数。
事实上,设 \(n=\prod\limits_{t=1}p_t^{k_t}\) ,则 \(\varphi(n)=\prod\limits_{t=1}\varphi(p_t^{k_t})=\prod\limits_{t=1} p_t^{k_t} \frac{p_t-1}{p_t}=n\prod\limits_{t=1}\frac{p_t-1}{p_t}\)
Euler函数可以用来给Fermat小定理升级成Euler定理
若 \(gcd(a,m)=1\) ,则 \(a^{\varphi(m)} \equiv 1 (mod m)\) 。
Euler函数是积性函数。
Dirichlet卷积
\(n=\sum_{d|n}\varphi(d)\)
也就是每个数=它所有因子的Euler函数之和。
注意积性函数的Dirichlet卷积也是积性函数,同时,Dirichlet卷积是积性函数的函数自己也是积性函数。
质因数分解法求Euler函数
const int MAXN = 1e6 + 10;
int p[MAXN], ptop;
int pm[MAXN];
void sieve(int n) {
pm[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!pm[i])
p[++ptop] = i, pm[i] = i;
for(int j = 1; j <= ptop; ++j) {
int t = i * p[j];
if(t > n)
break;
pm[t] = p[j];
if(i % p[j])
;
else
break;
}
}
}
int phi(int n) {
int res = n;
for(int i = 1; p[i]*p[i] <= n; ++i) {
if(n % p[i] == 0) {
res = res / p[i] * (p[i] - 1);
while(n % p[i] == 0)
n /= p[i];
}
}
if(n > 1)
res = res / n * (n - 1);
return res;
}
时间复杂度为 \(O(\sqrt{n})\) 预处理质数,然后 \(O(\frac{\sqrt{n}}{\log n})\) 单次求解Euler函数。
无论如何都要先筛质数,省掉这个log。
线性筛法求Euler函数
const int MAXN = 1e6 + 10;
int p[MAXN], ptop;
int pm[MAXN], pk[MAXN], phi[MAXN];
void sieve(int n) {
memset(pm, 0, sizeof(pm[0]) * (n + 1));
ptop = 0, pm[1] = 1, pk[1] = 1, phi[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!pm[i]) {
p[++ptop] = i, pm[i] = i;
pk[i] = i, phi[i] = i - 1;
}
for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; ++j) {
pm[t] = p[j];
if(i % p[j]) {
pk[t] = pk[p[j]];
phi[t] = phi[i] * phi[p[j]];
} else {
pk[t] = pk[i] * p[j];
phi[t] = (pk[t] == t) ? t - t / p[j] : phi[t / pk[t]] * phi[pk[t]];
break;
}
}
}
}
杜教筛求Euler函数前缀和