zoukankan      html  css  js  c++  java
  • NYOJ 998

    这道题是欧拉函数的使用,这里简要介绍下欧拉函数。

      欧拉函数定义为:对于正整数n,欧拉函数是指不超过n且与n互质的正整数的个数。

      欧拉函数的性质:1.设n = p1a1p2a2p3a3p4a4...pkak为正整数n的素数幂分解,那么φ(n) = n·(1-1/p1)·(1-1/p2)·(1-1/p3)···(1-1/pk)

              2.如果n是质数,则φ(n) = n-1;  反之,如果p是一个正整数且满足φ(p)=p-1,那么p是素数。

              3.设n是一个大于2 的正整数,则φ(n)是偶数

              4.当n为奇数时,有φ(2n)=φ(n)

              5.设m和n是互质的正整数,那么φ(mn)=φ(m)φ(n)

    (1)可以根据性质1,写出计算欧拉函数值的程序:  复杂度为O(√n)

     1 //直接求解欧拉函数
     2 int euler(int n){ //返回euler(n) 
     3      int res=n;
     4      for(int i=2;i*i<=n;i++){
     5         if(n%i==0){
     6             res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出 
     7             while(n%i==0) n/=i;
     8         }
     9      } 
    10      if(n>1) res=res/n*(n-1);
    11      return res;
    12 }

    (2)上面这种写法中,在for循环中选择i时,是顺序选择的。事实上性质1 中的p1、p2、p3、p4、...pk都是质数。如果在选择时,直接选择质数进行判断,那结果会优化很多。

    可以先把50000以内的素数用筛法选出来并保存,以方便欧拉函数使用。这样,在不考虑筛法的时间复杂度,而单看欧拉函数,其复杂度变为O(x),x为√n以内素数的个数。

     1 #include <cstring>
     2 bool boo[50000];int p[20000];
     3 
     4 void prim(){
     5     //线性筛素数
     6     memset(boo,0,sizeof(boo));
     7     boo[0]=boo[1]=1;
     8     int k=0;     
     9     for(int i=2;i<50000;i++){
    10         if(!boo[i]) p[k++]=i;
    11         for(int j=0;j<k&&i*p[j]<50000;j++){
    12             boo[i*p[j]] = 1;
    13             count++;
    14             if(!(i%p[j])) break;
    15         }
    16     } 
    17     
    18 } 
    19 
    20 int phi(int n){
    21     int rea = n;
    22     for(int i=0;p[i]*p[i]<=n;i++ ){  //对一些不是素数的可不用遍历   
    23         if(n%p[i]==0){
    24             rea = rea-rea/p[i];
    25             while(n%p[i]==0) n/=p[i];
    26         }    
    27     }
    28     if(n>1) rea-=rea/n;
    29     return rea;
    30 }

    (3)递推求欧拉函数

        如果频繁的要使用欧拉函数值,就需要预先打表。复杂度约为O(nlnn)

     1 //递推法打欧拉函数表   
     2 #define Max 1000001  
     3 int phi[Max];  
     4 void Init(){   
     5     for(int i=1;i<=Max;i++) phi[i]=i;
     6     for(int i=2;i<=Max;i+=2) phi[i]/=2;
     7     for(int i=3;i<=Max;i+=2)  
     8         if(phi[i]==i)  
     9             for(int j=i;j<=Max;j+=i)  
    10                phi[j]=phi[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出   
    11 }  

    应用:NYOJ 998   http://acm.nyist.net/JudgeOnline/problem.php?pid=998

    这道题的精华如何将符合条件的gcd(x,n)表达出来:见代码  d*Euler(n/d)  中为什么乘以d的解释  。 

    然后是遍历一遍小于n的数,测试每个符合的数加起来即可。其实还可以更快,观察发现,在能够整除n的i里面,相对应的n/i也有相似的性质,这样以来只需要遍历到sqrt(n)即可。

    网络摘抄代码如下:

     1  
     2 #include<iostream>
     3 #include<cstdio>
     4 using namespace std;
     5 
     6 typedef long long LL;
     7 LL Euler(LL n){
     8     LL ans = n;
     9     for(int i = 2; i * i <= n; i++){
    10         if(n % i == 0){
    11             ans = ans / i * (i-1);
    12             while(n % i == 0)
    13                 n /= i;
    14         }
    15     }
    16     if(n > 1) ans = ans / n * (n-1);
    17     return ans;
    18 }
    19 
    20 int main(){    
    21     LL n,m;
    22     while(cin>>n>>m){
    23         LL ans = 0;
    24         for(int i = 1; i * i <= n; i++){
    25             if(n % i == 0){
    26                 if(i >= m){
    27                     int d = i;
    28                     ans += d*Euler(n/d);
    29                     // 考虑 gcd(x,n)  1=<x<=n  
    30                     //这个的由来是  gcd(x/d,n/d) = 1.如果我们取一个能让n/d取整数的d的取值,于是我们
    31                     //取到了n%i==0的i,于是 能够满足gcd(x,n) = d 的x的个数为Euler(n/d)个
    32                     //那么在该gcd = d 的情况下需要加到ans里面的d的个数就是Euler(n/d)个 ,所以有ans+=d*Euler(n/d)
    33                      
    34                 }
    35                 if(i * i != n && n / i >= m){
    36                     int d = n / i;
    37                     ans += d*Euler(n/d);
    38                 }
    39             }
    40         }
    41         cout<<ans<<endl;
    42     }
    43     return 0;
    44 }
    45         
    
    
  • 相关阅读:
    log4j日志输出级别(转)
    spring-framework——hao123
    gradle配置国内镜像
    项目启动控制台严重: Error listenerStart问题定位
    Pycharm按键失灵
    Mixed Content混合内容错误 Iframe Http页面无法访问
    RSA加密公钥系数获取结果多00
    xml报文标签替换正则表达式
    AES采用CBC模式128bit加密工具类
    js进行MD5加密(含中文),与后台JAVA加密之后结果不同(解决)
  • 原文地址:https://www.cnblogs.com/liugl7/p/6246442.html
Copyright © 2011-2022 走看看