欧拉函数
ϕ
(
n
)
\phi(n)
ϕ(n):表示1~n中与n互质的数的个数
例如
ϕ
(
6
)
=
2
\phi(6)=2
ϕ(6)=2,因为1 2 3 4 5 6中只有1和5跟6互质。
注意1和任何数互质,1的欧拉函数是1。
求
ϕ
(
n
)
\phi(n)
ϕ(n)方法如下:
当
n
=
p
1
a
1
∗
p
2
a
2
∗
.
.
.
∗
p
k
a
k
n=p_1^{a_1}*p_2^{a_2}*...*p_k^{a_k}
n=p1a1∗p2a2∗...∗pkak时,其中
p
p
p均为质数
ϕ
(
n
)
=
n
∗
(
1
−
1
p
1
)
∗
(
1
−
1
p
2
)
∗
.
.
.
∗
(
1
−
1
p
k
)
\phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*...*(1-\frac{1}{p_k})
ϕ(n)=n∗(1−p11)∗(1−p21)∗...∗(1−pk1)
例如
ϕ
(
6
)
=
6
∗
(
1
−
1
2
)
∗
(
1
−
1
3
)
=
6
∗
1
2
∗
2
3
=
2
\phi(6)=6*(1-\frac{1}{2})*(1-\frac{1}{3})=6*\frac{1}{2}*\frac{2}{3}=2
ϕ(6)=6∗(1−21)∗(1−31)=6∗21∗32=2
下面证明为什么
ϕ
(
n
)
=
n
∗
(
1
−
1
p
1
)
∗
(
1
−
1
p
2
)
∗
.
.
.
∗
(
1
−
1
p
k
)
\phi(n) = n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*...*(1-\frac{1}{p_k})
ϕ(n)=n∗(1−p11)∗(1−p21)∗...∗(1−pk1):
求1 ~ n中与n互质的数的个数,相当于求从1 ~ n中去掉所有因数有
p
1
,
p
2
,
.
.
.
,
p
k
p_1,p_2,...,p_k
p1,p2,...,pk的数后剩下多少个数。从一个集合里去掉若干元素,可以想到容斥原理。
1、先从1 ~ n中去掉
p
1
,
p
2
,
.
.
.
,
p
k
p_1,p_2,...,p_k
p1,p2,...,pk的所有倍数,即
n
−
n
p
1
−
n
p
2
−
.
.
.
−
n
p
k
n-\frac{n}{p_1}- \frac{n}{p_2}-...- \frac{n}{p_k}
n−p1n−p2n−...−pkn。此处所有
n
p
k
\frac{n}{p_k}
pkn都是向下取整。
在这个步骤中,当一个数同时是多个
p
p
p的倍数时,它会被去除很多次。
2、加上所有
p
i
∗
p
j
p_i * p_j
pi∗pj的倍数,即
n
−
n
p
1
−
n
p
2
−
.
.
.
−
n
p
k
+
n
p
1
p
2
+
n
p
1
p
3
+
.
.
.
+
n
p
k
−
1
p
k
n-\frac{n}{p_1}- \frac{n}{p_2}-...- \frac{n}{p_k} +\frac{n}{p_1p_2}+\frac{n}{p_1p_3}+...+\frac{n}{p_{k-1}p_k}
n−p1n−p2n−...−pkn+p1p2n+p1p3n+...+pk−1pkn
这一步加回去了同时是2个
p
p
p的倍数的数,那么还要接着减去同时是3个
p
p
p的倍数的数。
不妨设这个数是
p
1
∗
p
2
∗
p
3
p_1*p_2*p_3
p1∗p2∗p3,在步骤1,这个数被减了3次,在步骤2,这个数又被
p
1
p
2
,
p
1
p
3
,
p
2
p
3
p_1p_2,p_1p_3,p_2p_3
p1p2,p1p3,p2p3加了三次,不减也不加,但这个数最终应该被减1次。
3、加上所有 p i ∗ p j ∗ p k p_i * p_j *p_k pi∗pj∗pk的倍数 n − n p 1 − n p 2 − . . . − n p k + n p 1 p 2 + n p 1 p 3 + . . . + n p k − 1 p k − n p 1 p 2 p 3 − n p 1 p 2 p 4 − . . . − n p k − 2 p k − 1 p k n-\frac{n}{p_1}- \frac{n}{p_2}-...- \frac{n}{p_k} +\frac{n}{p_1p_2}+\frac{n}{p_1p_3}+...+\frac{n}{p_{k-1}p_k} - \frac{n}{p_1p_2p_3}-\frac{n}{p_1p_2p_4}-...-\frac{n}{p_{k-2}p_{k-1}p_k} n−p1n−p2n−...−pkn+p1p2n+p1p3n+...+pk−1pkn−p1p2p3n−p1p2p4n−...−pk−2pk−1pkn。同理还要接着加上同时是4个 p p p的倍数的数。
依次类推,最终
n
−
n
p
1
−
n
p
2
−
.
.
.
−
n
p
k
+
n
p
1
p
2
+
n
p
1
p
3
+
.
.
.
+
n
p
k
−
1
p
k
−
n
p
1
p
2
p
3
−
n
p
1
p
2
p
4
−
.
.
.
−
n
p
k
−
2
p
k
−
1
p
k
+
.
.
.
n-\frac{n}{p_1}- \frac{n}{p_2}-...- \frac{n}{p_k} +\frac{n}{p_1p_2}+\frac{n}{p_1p_3}+...+\frac{n}{p_{k-1}p_k} - \frac{n}{p_1p_2p_3}-\frac{n}{p_1p_2p_4}-...-\frac{n}{p_{k-2}p_{k-1}p_k}+...
n−p1n−p2n−...−pkn+p1p2n+p1p3n+...+pk−1pkn−p1p2p3n−p1p2p4n−...−pk−2pk−1pkn+...
=
n
(
1
−
1
p
1
−
1
p
2
−
.
.
.
−
1
p
k
+
1
p
1
p
2
+
1
p
1
p
3
+
.
.
.
+
1
p
k
−
1
p
k
−
1
p
1
p
2
p
3
−
1
p
1
p
2
p
4
−
.
.
.
−
1
p
k
−
2
p
k
−
1
p
k
+
.
.
.
1
p
1
p
2
.
.
.
p
k
)
=n(1-\frac{1}{p_1}- \frac{1}{p_2}-...- \frac{1}{p_k} +\frac{1}{p_1p_2}+\frac{1}{p_1p_3}+...+\frac{1}{p_{k-1}p_k} - \frac{1}{p_1p_2p_3}-\frac{1}{p_1p_2p_4}-...-\frac{1}{p_{k-2}p_{k-1}p_k}+...\frac{1}{p_{1}p_{2}...p_k})
=n(1−p11−p21−...−pk1+p1p21+p1p31+...+pk−1pk1−p1p2p31−p1p2p41−...−pk−2pk−1pk1+...p1p2...pk1)
这个式子就是
n
∗
(
1
−
1
p
1
)
∗
(
1
−
1
p
2
)
∗
.
.
.
∗
(
1
−
1
p
k
)
n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*...*(1-\frac{1}{p_k})
n∗(1−p11)∗(1−p21)∗...∗(1−pk1)展开后的式子,通过对比各项之前的系数即可知道,如
1
p
1
p
2
\frac{1}{p_1p_2}
p1p21系数是
+
1
+1
+1;
n
(
1
−
1
p
1
)
(
1
−
1
p
2
)
.
.
.
(
1
−
1
p
k
)
n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})
n(1−p11)(1−p21)...(1−pk1)中
−
1
p
1
-\frac{1}{p_1}
−p11和
−
1
p
2
-\frac{1}{p_2}
−p21相乘,其他项全取1,系数也是
+
1
+1
+1。
该题用公式求即可,分解质因数时间复杂度 a = 1 0 4 \sqrt a=10^4 a=104,再乘上n=100是 1 0 6 10^6 106,符合要求。
#include <iostream>
using namespace std;
int n, a;
int main() {
scanf("%d", &n);
while(n -- ) {
scanf("%d", &a);
double ans = a;
for (int i = 2; i * i <= a; i ++ ) {
int prime = 0;
while(a % i == 0) {
a /= i;
prime = i;
}
if (prime) ans *= 1 - 1.0 / i; // 只有是质数时才进行运算
}
if (a > 1) ans *= 1 - 1.0 / a; // a本身是质数
printf("%.0lf\n", ans);
}
return 0;
}
实际上不需要小数运算,只要将 a n s ∗ ( 1 − 1 / p k ) ans*(1-1/p_k) ans∗(1−1/pk)改成 a n s / p k ∗ ( p k − 1 ) ans/p_k*(p_k-1) ans/pk∗(pk−1)即可避免小数。
while(n -- ) {
scanf("%d", &a);
int ans = a;
for (int i = 2; i * i <= a; i ++ ) {
if (a % i == 0) {
ans = ans / i * (i - 1);
while (a % i == 0) a /= i;
}
}
if (a > 1) ans = ans / a * (a - 1); // a本身是质数
printf("%d\n", ans);
}