【刷题】数学知识——欧拉函数

欧拉函数 ϕ ( 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=p1a1p2a2...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(1p11)(1p21)...(1pk1)
例如 ϕ ( 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(121)(131)=62132=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(1p11)(1p21)...(1pk1)
求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} np1np2n...pkn。此处所有 n p k \frac{n}{p_k} pkn都是向下取整。
在这个步骤中,当一个数同时是多个 p p p的倍数时,它会被去除很多次。

2、加上所有 p i ∗ p j p_i * p_j pipj的倍数,即 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} np1np2n...pkn+p1p2n+p1p3n+...+pk1pkn
这一步加回去了同时是2个 p p p的倍数的数,那么还要接着减去同时是3个 p p p的倍数的数。
不妨设这个数是 p 1 ∗ p 2 ∗ p 3 p_1*p_2*p_3 p1p2p3,在步骤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 pipjpk的倍数 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} np1np2n...pkn+p1p2n+p1p3n+...+pk1pknp1p2p3np1p2p4n...pk2pk1pkn。同理还要接着加上同时是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}+... np1np2n...pkn+p1p2n+p1p3n+...+pk1pknp1p2p3np1p2p4n...pk2pk1pkn+...
= 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(1p11p21...pk1+p1p21+p1p31+...+pk1pk1p1p2p31p1p2p41...pk2pk1pk1+...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(1p11)(1p21)...(1pk1)展开后的式子,通过对比各项之前的系数即可知道,如 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(1p11)(1p21)...(1pk1) − 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(11/pk)改成 a n s / p k ∗ ( p k − 1 ) ans/p_k*(p_k-1) ans/pk(pk1)即可避免小数。

    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);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值