NYOJ 998
這道題是歐拉函數(shù)的使用,這里簡(jiǎn)要介紹下歐拉函數(shù)。
歐拉函數(shù)定義為:對(duì)于正整數(shù)n,歐拉函數(shù)是指不超過(guò)n且與n互質(zhì)的正整數(shù)的個(gè)數(shù)。
歐拉函數(shù)的性質(zhì):1.設(shè)n = p1a1p2a2p3a3p4a4...pkak為正整數(shù)n的素?cái)?shù)冪分解,那么φ(n) = n·(1-1/p1)·(1-1/p2)·(1-1/p3)···(1-1/pk)
2.如果n是質(zhì)數(shù),則φ(n) = n-1; ?反之,如果p是一個(gè)正整數(shù)且滿足φ(p)=p-1,那么p是素?cái)?shù)。
3.設(shè)n是一個(gè)大于2 的正整數(shù),則φ(n)是偶數(shù)
4.當(dāng)n為奇數(shù)時(shí),有φ(2n)=φ(n)
5.設(shè)m和n是互質(zhì)的正整數(shù),那么φ(mn)=φ(m)φ(n)
(1)可以根據(jù)性質(zhì)1,寫出計(jì)算歐拉函數(shù)值的程序: ?復(fù)雜度為O(√n)
1 //直接求解歐拉函數(shù) 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);//先進(jìn)行除法是為了防止中間數(shù)據(jù)的溢出 7 while(n%i==0) n/=i; 8 } 9 } 10 if(n>1) res=res/n*(n-1); 11 return res; 12 }?
(2)上面這種寫法中,在for循環(huán)中選擇i時(shí),是順序選擇的。事實(shí)上性質(zhì)1 中的p1、p2、p3、p4、...pk都是質(zhì)數(shù)。如果在選擇時(shí),直接選擇質(zhì)數(shù)進(jìn)行判斷,那結(jié)果會(huì)優(yōu)化很多。
可以先把50000以內(nèi)的素?cái)?shù)用篩法選出來(lái)并保存,以方便歐拉函數(shù)使用。這樣,在不考慮篩法的時(shí)間復(fù)雜度,而單看歐拉函數(shù),其復(fù)雜度變?yōu)镺(x),x為√n以內(nèi)素?cái)?shù)的個(gè)數(shù)。
?
1 #include <cstring> 2 bool boo[50000];int p[20000]; 3 4 void prim(){ 5 //線性篩素?cái)?shù) 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++ ){ //對(duì)一些不是素?cái)?shù)的可不用遍歷 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)遞推求歐拉函數(shù)
如果頻繁的要使用歐拉函數(shù)值,就需要預(yù)先打表。復(fù)雜度約為O(nlnn)
1 //遞推法打歐拉函數(shù)表 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);//先進(jìn)行除法是為了防止中間數(shù)據(jù)的溢出 11 }?
?
應(yīng)用:NYOJ 998 ??http://acm.nyist.net/JudgeOnline/problem.php?pid=998
這道題的精華如何將符合條件的gcd(x,n)表達(dá)出來(lái):見(jiàn)代碼? d*Euler(n/d) ?中為什么乘以d的解釋 ?。?
然后是遍歷一遍小于n的數(shù),測(cè)試每個(gè)符合的數(shù)加起來(lái)即可。其實(shí)還可以更快,觀察發(fā)現(xiàn),在能夠整除n的i里面,相對(duì)應(yīng)的n/i也有相似的性質(zhì),這樣以來(lái)只需要遍歷到sqrt(n)即可。
網(wǎng)絡(luò)摘抄代碼如下:
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 //這個(gè)的由來(lái)是 gcd(x/d,n/d) = 1.如果我們?nèi)∫粋€(gè)能讓n/d取整數(shù)的d的取值,于是我們 31 //取到了n%i==0的i,于是 能夠滿足gcd(x,n) = d 的x的個(gè)數(shù)為Euler(n/d)個(gè) 32 //那么在該gcd = d 的情況下需要加到ans里面的d的個(gè)數(shù)就是Euler(n/d)個(gè) ,所以有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?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/liugl7/p/6246442.html
總結(jié)
- 上一篇: 程序中保存状态的方式之Cookies
- 下一篇: 【TweenMax】实例Timeline