歐拉函數這里理論性非常強,它與費馬小定理、剩余系、素數分解定理聯系,能夠推導出一系列的定理。




計算phi(n)的編碼實現:
#include<cstdlib> #include<iostream> using namespace std; int phi(int n) { int rea = n; for(int i = 2;i*i <=n;i++) if(n%i == 0) { rea = rea - rea/i; do n /= i; while(n%i == 0); } if(n > 1) rea = rea - rea/n; return rea; } int main() { int n; while(cin >> n && n) { cout << phi(n) << endl; } return 0; }
計算區間[1,n]上歐拉函數值的和phi(2)+phi(3)+…+phi(n):
當n取得較大整數時,如果用上文求單個整數的歐拉函數值然后相加,耗時太多,這里對於求區間歐拉函數值的和,有一個類似Eratosthenes篩法的優化。
那么這里我們就像篩選素數那樣,得到一個素數然后設置第二層循環記錄這個素數整數倍的整數的“不完整歐拉值”,當該整數所有的素因子都遍歷到,歐拉值便更新到真實值。
#include <cstdio> //O(nloglogn) #include <cstdlib> #include <cstring> #include <cmath> using namespace std; const int SIZE = 1000000 + 5; int phi[SIZE]; void init() { int i, j; memset(phi, 0, sizeof(phi)); phi[1] = 1; for(int i = 2; i < SIZE; i++) if(!phi[i]) { for(j = i; j < SIZE; j+=i) { if(!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i-1); } } } int main() { init(); int n; while(scanf("%d",&n)!=EOF && n) { long long sum = 0; for(int i = 2; i <= n; i++) { sum += phi[i]; } printf("%lld\n",sum); } }
應用1:既約真分數(poj 2478).
給出整數n,讓你求解分母小於n的所有既約真分數的個數。
分析:首先我們要搞懂什么是既約真分數,簡單來說,就是小於1的最簡分數。那么我們很容易將其與歐拉函數聯系起來,因為對於一個分母為n的既約真分數的個數,實際上就是phi(n),那么這個問題本質上就是求解phi(2)+...+phi(n).
應用2:精簡打表數據.(uva 10820)
有一道比賽題目,輸入兩個整數x、y(均小於等於n),輸出某個函數值f(x,y),一位選手想打表,但是如果全部打出來的話會造成內存超限,需要精簡。
這道題目可以通過f(x,y)計算出f(kx,ky),k是任意正整數,這樣很多結果就不需要放在表中了。
分析:通過“f(x,y)計算f(x,y)”這個題設條件,我們就能夠將其聯想到歐拉函數。最終表中存的二元組(x,y)只要互素,就能夠保證表中不存在任何“贅余(即可由表中的另外某組數據計算得來)”數據.
假設x>y,那么我們枚舉x=2、3、…、n,二元組的數量應該是phi(2)+…+phi(n),由對稱性,最終結果應該乘2,而且不要忘記了(1,1)這個特殊情況。
最終結果應該是2(phi(2)+…+phi(n)) + 1.
應用3:公約數之和(uva 11426)
給出整數n∈[2,4000000],求解∑gcd(i,j),其中(i,j)滿足1≤i<j≤n.
分析:

應用4:階乘的歐拉函數值(uva 11440)。
給出整數n , m,n∈[2,10^7],n≥m≥1,n-m≤10^5.那么請問[2,N!]有多少個x滿足下列的性質,x的所有素因子都大於M.
分析:


參考代碼如下:
#include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn = 10000000 + 5; const int MOD = 100000007; int vis[maxn] , phifac[maxn]; void gen_primes(int n) { int m = (int)sqrt(n+0.5); int c = 0; memset(vis, 0, sizeof(vis)); for(int i = 2; i <= m; i++) if(!vis[i]){ for(int j = i*i; j <= n; j+=i) vis[j] = 1; } } int main() { int n , m; gen_primes(maxn); phifac[1] = phifac[2] = 1; for(int i = 3;i < maxn;i++) phifac[i] = ((long long)phifac[i-1] *(vis[i] ? i : i - 1)) %MOD;//題設給出取余運算MOD,中間過程一定要小心不要溢出。 while(scanf("%d%d",&n , &m) && n) { int ans = phifac[m]; for(int i = m + 1;i <= n;i++) ans = (long long)ans*i%MOD; printf("%d\n",(ans - 1 + MOD)%MOD); } return 0; }
