【复习】欧拉函数

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

  • 写作\(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\).
posted @ 2018-12-02 19:56  maomao9173  阅读(213)  评论(0编辑  收藏  举报