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?
?
?
轉載于:https://www.cnblogs.com/liugl7/p/6246442.html
總結
- 上一篇: 程序中保存状态的方式之Cookies
- 下一篇: 【TweenMax】实例Timeline