Bootstrap

欧拉函数(phi)

欧拉函数的定义

欧拉函数 φ ( N ) \varphi(N) φ(N),指 1 − N 1-N 1N 中与 N N N 互质的数的个数。
N = 1 N=1 N=1 时, φ ( N ) = 1 \varphi(N)=1 φ(N)=1 。当 N N N 为质数, φ ( N ) = N − 1 \varphi(N)=N-1 φ(N)=N1

特殊性质

欧拉函数是积性函数,也就是对于两个互质数 x x x y y y, φ ( x × y ) = φ ( x ) × φ ( y ) \varphi(x\times y)=\varphi(x)\times \varphi(y) φ(x×y)=φ(x)×φ(y)

N N N 是一个大于 2 2 2 的整数并且 φ ( N ) \varphi(N) φ(N) 是偶数,那么与 N N N 互质的数的和是 N × φ ( N ) 2 \frac{N\times\varphi(N)}{2} 2N×φ(N)

∑ p ∣ N φ ( p ) = N \sum_{p|N}\varphi(p)=N pNφ(p)=N

如果 x x x y y y 互质,那么 y ∣ ( x φ ( y ) − 1 ) y|(x^{\varphi(y)}-1) y(xφ(y)1)

朴素求 φ ( N ) \varphi(N) φ(N)

据唯一分解定理知 N = a 1 b 1 × . . . × a k b k N=a_1^{b_1}\times...\times a_k^{b_k} N=a1b1×...×akbk

也知 φ ( a 1 b 1 × . . . × a k b k ) = φ ( a 1 b 1 ) × . . . × φ ( a k b k ) \varphi(a_1^{b_1}\times...\times a_k^{b_k})=\varphi(a_1^{b_1})\times...\times\varphi(a_k^{b_k}) φ(a1b1×...×akbk)=φ(a1b1)×...×φ(akbk)

又知 φ ( a i b i ) = a i b i − a i b i − 1 \varphi(a_i^{b_i})=a_i^{b_i}-a_i^{b_i-1} φ(aibi)=aibiaibi1

那么 φ ( n ) = ( a 1 b 1 − a 1 b i − 1 ) × . . . × ( a k b k − a k b k − 1 ) \varphi(n)=(a_1^{b_1}-a_1^{b_i-1})\times...\times(a_k^{b_k}-a_k^{b_k-1}) φ(n)=(a1b1a1bi1)×...×(akbkakbk1)

把括号拆开 φ ( n ) = a 1 b 1 × ( 1 − 1 a 1 ) × . . . × a k b k × ( 1 − 1 a k ) \varphi(n)=a_1^{b_1}\times(1-\frac{1}{a_1})\times...\times a_k^{b_k}\times(1-\frac{1}{a_k}) φ(n)=a1b1×(1a11)×...×akbk×(1ak1)

最后 φ ( n ) = a 1 b 1 × . . . × a k b k × ( 1 − 1 a 1 ) × . . . × ( 1 − 1 a k ) = N × Π i = 1 N ( 1 − 1 a i ) \varphi(n)=a_1^{b_1}\times...\times a_k^{b_k}\times(1-\frac{1}{a_1})\times...\times(1-\frac{1}{a_k})=N\times\Pi_{i=1}^N(1-\frac{1}{a_i}) φ(n)=a1b1×...×akbk×(1a11)×...×(1ak1)=N×Πi=1N(1ai1)

朴素代码

int ans = n;
for (int i = 2; i * i <= n; i++) {
	while (n % i == 0) {
		ans = ans / i * (i - 1);
		n /= i;
	}
}
if (n > 1) ans = ans / n * (n - 1);

筛法求 φ ( N ) \varphi(N) φ(N)

就是一边筛质数,一边求 φ \varphi φ,可以求出 1 − N 1-N 1N 内的 φ \varphi φ,时间约 O ( N log ⁡ N ) O(N\log N) O(NlogN)

memset(a, true, sizeof(a));
k = 0, phi[1] = 1, a[1] = false;
for (int i = 2; i <= n; i++) {
	if (a[i]) pri[++k] = i, phi[i] = i - 1;
	for (int j = 1; j <= k && i * pri[j] <= n; j++) {
		a[i * pri[j]] = false;
		if (i % pri[j]) phi[i * pri[j]] = phi[i] * phi[pri[j]];
		else {
			phi[i * pri[j]] = phi[i] * pri[j];
			break;
		}
	}
}
;