zoukankan      html  css  js  c++  java
  • 【复习】欧拉函数

    首先让我们来复习以下欧拉函数的概念。

    • 写作(phi(i)),表示小于(i)的与(i)互质的数的个数
    • 特殊的,(phi(1)=1);

    根据定义我们可以得到其推导方法。

    • 对于任意的(i∈[2,INF]),(i)都可以被拆分为(p1^{c1}*p2^{c2}*...pn^{cn})的形式,其中(pi)表示素数,而(ci)表示素数的次数,即把一个数拆成素数乘积的形式。
    • 所以利用容斥原理,我们可得(phi(i))的推导:(phi(i)=N(1-1/p1)...(1-1/pn));

    它有一些很优秀的性质:

    • (phi)是积性函数。对于任意满足(gcd(a,b)=1)(a,b),都满足下面这个式子:(phi(ab)=phi(a)phi(b));
    • (p|n)(p^2|n),则有(phi(n)=phi(n/p)*p)
      • 证明:若满足以上条件,则n和n/p的素数组成相同,不同的只有次数。根据定义式,可以得到(phi(n)=phi(n/p)*p).
    • (p|n)(p^2)不被(n)整除,则有(phi(n)=phi(n/p)*(p-1))
      • 证明:若满足(p|n)(p^2)不被(n)整除,则(n)(n/p)互质。满足(phi(n)=phi(n/p)*phi(p)=phi(n)=phi(n/p)*(p-1))

    那么关键来了:我们要利用这些性质求解欧拉函数。

    利用定义式,我们可以很容易想到常规推导:

        for(register int i=1;i<=n;++i)phi[i]=i;//先记为其本身
        for(register int i=2;i<=n;++i){
            if(phi[i]==i){//质数
                for(register int j=i;j<=n;j+=i){//处理后面的每一个i的倍数 
                    phi[j]=phi[j]/i*(i-1);//利用定义式计算非积性求解情况 
                }
            }
        }
    }
    

    这个是建立在埃氏筛基础上的欧拉函数求法,复杂度是O(nlogn),足以水过P2158 40000 的数据范围。但是如果数据更大的话,这种算法很显然是不优秀的,我们就要考虑更快的算法,于是便想到了同一个人名字命名的欧拉筛。(欧拉全家桶.jpg)

    首先先考虑简单的欧拉筛求素数集。

        vis[1]=1;//vis记录素数情况,0为素数
        for(register int i=2;i<=n;++i){
             if(!vis[i])prime[++tot]=i;//素数
             for(register int j=1;j<=tot&&i*prime[j]<=n;++j){
                   vis[i*prime[j]]=1;//合数标为1
                   if(i%prime[j]==0)break;//j以后的都可以由更小的素数筛得
                   //如i*prime[j+1]中i本身可以被分解为比prime[j+1]更小的质数。
             }
        }
    

    同理,很容易就可以想到怎么对欧拉函数求解了。

        for(register int i=1;i<=n;++i)phi[i]=i;
        for(register int i=2;i<=n;++i){
            if(phi[i]==i){//i为质数 
                prime[++cnt]=i;//记录这个数位质数,最小质因子是它自己 
                phi[i]=i-1;
            }
            for(register int j=1;j<=cnt&&i*prime[j]<=n;++j){//不超出n的范围
                if(i%prime[j]!=0){
                    phi[i*prime[j]]=phi[i]*(prime[j]-1);
                    //如果i%prime[j]!=0,则i*prime[j]和prime[j]互质 
                }else{
                    phi[i*prime[j]]=phi[i]*(prime[j]);
                    break;
                }
            } 
        }
    

    就是这样~

    什么时候会用到欧拉函数呢?通常我们要把它从复杂的模型里提取出来。例如[P2158 SDOI2008]仪仗队这个题目,就需要我们想到,对于任意一个首次出现的斜率,其gcd(x,y)一定为1即满足互质。认真思考后就会发现完全就是一个欧拉函数求和啦~


    (UPD):关于欧拉筛的那个(break)作用的考虑。

    毫不夸张的说,欧拉筛中的那个(break)是整个算法中最精华最让人赞叹的地方。下面我们来考虑一下这种情况:

    • (prime[ ])中存储了所有的素数
    • 在欧拉筛中,一旦出现(i\%prime[j]==0),就在进行完本次运算后停止。
    • 原因:既然已经有(i\%prime[j]=0),那么(prime[j])就是(i)的本身组成。在继续往后找的过程中,(prime[j])只会越来越大。为了符合只让(i)被其最小质因子筛掉一次的条件,我们在做完这次循环后就(break).
  • 相关阅读:
    js数组求交集
    php安装oci8和pdo_oci扩展实现连接oracle数据库
    nginx配置静态资源压缩
    SHELL递归遍历文件夹下所有文件
    PHP函数获取临时文件目录
    php去除文件bom头
    tcpdump抓取udp报文
    linux获取当前运行级别
    当安装软件后提示依赖没有安装时
    Ubuntu卸载通过apt-get命令安装的软件
  • 原文地址:https://www.cnblogs.com/maomao9173/p/10054921.html
Copyright © 2011-2022 走看看