博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NYOJ 998
阅读量:5068 次
发布时间:2019-06-12

本文共 3368 字,大约阅读时间需要 11 分钟。

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

  欧拉函数定义为:对于正整数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 
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
<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
3 #include
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=
<=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<
<

 

 

 

 

转载于:https://www.cnblogs.com/liugl7/p/6246442.html

你可能感兴趣的文章
goto命令
查看>>
第2周学习进度
查看>>
修改系统的shell
查看>>
Opencv DNN 物体检测
查看>>
C++定义动态数组
查看>>
步步为营-84-数字转化为金额的Js+enter键取消页面刷新
查看>>
插入排序
查看>>
反刍我的傻瓜时代(四)
查看>>
try...catch...
查看>>
IE6中 PNG 背景透明的最佳解决方案
查看>>
easyui设置行的背景色
查看>>
JavaScript学习总结【8】、面向对象编程
查看>>
【HackerRank】Gem Stones
查看>>
Octopress技巧之设置关键字和描述
查看>>
ajax学习
查看>>
数据库的优化
查看>>
【转】tar打包解压详解
查看>>
【hadoop】【demo】HBase shell
查看>>
GTK: about Building GTK+ 3.0
查看>>
MySQL 5.7的安装及主从复制(主从同步)
查看>>