摘要
本文主要講述了算術基本定理的內容,具體的應用形式,重點結合例題展示如何使用算術基本定理求解問題。
算術基本定理
算術基本定理可表述為:任何一個大於1的自然數 N,如果N不為質數,那么N可以唯一分解成有限個質數的乘積N=P1a1P2a2P3a3......Pnan,這里P1<P2<P3......<Pn均為質數,其中指數ai是正整數。這樣的分解稱為 N 的標准分解式。
算術基本定理是初等數論中一條非常基本和重要的定理,它把對自然數的研究轉化為對其最基本的元素——素數的研究。唯一因子分解的思想從本質上講是指以下兩種性質: “存在性和唯一性”。所謂“存在性”就是指一個元素可以分解為有限多個不可約因子的乘積;“唯一性”是指這種分解表示在某種意義上來說是唯一的。
定理應用
算法實現
1 typedef long long ll; 2 const int maxn = 1e6 + 7; 3 ll a[maxn], b[maxn];//a[i]表示第i個質因子,b[i]表示第i個質因子的指數 4 void fac(ll n, int& tot) {//待分解的整數和不同質因數的個數(按引用傳遞) 5 ll tmp = (ll)(sqrt(n) + 0.5); 6 tot = 0; 7 ll now = n; 8 for(int i = 2; i <= tmp; i++) { 9 if(now % i == 0) { 10 a[++tot] = i; 11 b[tot] = 0; 12 while(now % i == 0) { 13 ++b[tot]; 14 now /= i; 15 } 16 } 17 } 18 if(now != 1) {//如果剩下的不是1,那就是最大的質因數 19 a[++tot] = now; 20 b[tot] = 1; 21 } 22 }
可以用如下代碼直接輸出2 到100的質因數分解結果

1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 using namespace std; 5 6 typedef long long ll; 7 const int maxn = 1e6 + 7; 8 ll a[maxn], b[maxn];//a[i]表示第i個質因子,b[i]表示第i個質因子的指數 9 void fac(ll n, int& tot) {//待分解的整數和不同質因數的個數(按引用傳遞) 10 ll tmp = (ll)(sqrt(n) + 0.5); 11 tot = 0; 12 ll now = n; 13 for(int i = 2; i <= tmp; i++) { 14 if(now % i == 0) { 15 a[++tot] = i; 16 b[tot] = 0; 17 while(now % i == 0) { 18 ++b[tot]; 19 now /= i; 20 } 21 } 22 } 23 if(now != 1) {//如果剩下的不是1,那就是最大的質因數 24 a[++tot] = now; 25 b[tot] = 1; 26 } 27 } 28 29 int main() 30 { 31 for(ll i = 2; i <=100; i++) { 32 printf("%lld = ", i); 33 int tot = 0; 34 fac(i, tot); 35 for(int i = 1; i <= tot; i++) { 36 printf("%lld^%lld %c ", a[i], b[i], i == tot ? '\n' : '+'); 37 } 38 } 39 return 0; 40 }
例題解析
lightOJ 1341 Aladdin and the Flying Carpet
題意
給出一個長方形的面積a(不是正方形),給出該長方形最小的邊b,問組成該面積的長方形有多少種組合方案。比如12 2,有{2,6}、{3,4}兩種組合方案。
解題思路
問有多少種組合方案,其實就是面積a有多少對因子的乘積等於a,然后去掉最小邊不滿足條件的對兒數。普通暴力尋找因子對兒數的方法,肯定是要超時的。這里使用唯一分解定理的第一個應用,計算出總的因子數,然后除以2,再減去不符合條件的因子對數即可。需要注意的是每次不必全部試除一遍,不然會超時,這就是這種方法的時間優化之處。
代碼
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 typedef long long ll; 5 const ll maxn = 1e6 +7; 6 bool isp[maxn]; 7 int pis[maxn], tot; 8 9 void getp(ll n) {//得到n以內的素數及個數 10 memset(isp, true, sizeof(isp)); 11 for(ll i = 2; i <= n; i++) { 12 if(isp[i]) { 13 tot++; 14 pis[tot] = i; 15 } 16 for(int j = 1; (j <= tot) && (i * pis[j] <= n); j++) { 17 isp[i * pis[j]] = false; 18 if(i % pis[j] == 0) break; 19 } 20 } 21 } 22 ll fac(ll n) {//計算n的正因子個數之和 23 ll now = n; 24 ll ans = 1; 25 for(ll i = 1; i <= tot; i++) { 26 if(pis[i] > now)//重要剪枝,每次不必全部試除一遍才結束 27 break; 28 if(now % pis[i] == 0) { 29 int cnt = 0; 30 while(now % pis[i] == 0) { 31 cnt++; 32 now /= pis[i]; 33 } 34 ans *= (cnt + 1); 35 } 36 } 37 if(now != 1) 38 ans *= 1 + 1; 39 return ans; 40 } 41 ll solve(ll S, ll b) { 42 if(b * b >= S) 43 return 0; 44 45 ll ans = fac(S);//得到S的正因子個數之和 46 ans /= 2; 47 for(ll i = 1; i < b; i++) { 48 if(S % i == 0) 49 ans--; 50 } 51 return ans; 52 } 53 int main() 54 { 55 ll tot = 0; 56 getp(maxn - 7);//得到maxn - 7以內的素數及個數 57 int T, k = 1; 58 scanf("%d", &T); 59 while(T--) { 60 ll a, b; 61 scanf("%lld%lld", &a, &b); 62 printf("Case %d: %lld\n", k++, solve(a, b)); 63 } 64 return 0; 65 }
lightOJ 1236 Pairs Forming LCM
題意
給一個n(1 ≤ n ≤ 1014),問滿足lcm(i, j) = n (1 =< i, j <= n 且 i <= j)的(i, j)有多少對
解題思路
一看n的大小就知道暴力肯定是不行了,我們試着用算術基本定理求解一下,將n = p1^x1 * p2^x2 * p3^x3...ps^xs(其中s是唯一分解式中所有質因子的個數)
先假設n = p1^x1;
要使 lcm(i, j) = n,只有兩種方法:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 typedef long long ll; 5 const ll maxn = 1e7 +7; 6 bool isp[maxn]; 7 int pis[maxn/10], tot; 8 9 void getp(ll n) {//得到n以內的素數及個數 10 memset(isp, true, sizeof(isp)); 11 for(ll i = 2; i <= n; i++) { 12 if(isp[i]) { 13 tot++; 14 pis[tot] = i; 15 } 16 for(int j = 1; (j <= tot) && (i * pis[j] <= n); j++) { 17 isp[i * pis[j]] = false; 18 if(i % pis[j] == 0) break; 19 } 20 } 21 } 22 ll fac(ll n) {//計算n的正因子個數之和 23 ll now = n; 24 ll ans = 1; 25 for(ll i = 1; i <= tot; i++) { 26 if(pis[i] > now)//重要剪枝,每次不必全部試除一遍才結束 27 break; 28 if(now % pis[i] == 0) { 29 int cnt = 0; 30 while(now % pis[i] == 0) { 31 cnt++; 32 now /= pis[i]; 33 } 34 ans *= ( 2 * cnt + 1); 35 } 36 } 37 if(now != 1) 38 ans *= 2 * 1 + 1; 39 40 return ans; 41 } 42 ll solve(ll n) { 43 ll ans = fac(n);//得到n的正因子個數之和 44 return ans / 2 + 1; 45 } 46 int main() 47 { 48 ll tot = 0; 49 getp(maxn - 7);//得到maxn - 7以內的素數及個數 50 int T, k = 1; 51 scanf("%d", &T); 52 while(T--) { 53 ll n; 54 scanf("%lld", &n); 55 printf("Case %d: %lld\n", k++, solve(n)); 56 } 57 return 0; 58 }
題意
給出n(1 ≤ n ≤ 108)計算出調和級數的結果
解題思路
雖然沒有直接的公式,但是歐拉曾給出過一個近似公式計算調和級數的和,但是由於前幾項誤差較大,所以我們先計算前10000的結果,之后的使用公式計算。
代碼
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 5 const int maxn = 100010; 6 using namespace std; 7 const double C = 0.57721566490153286060651209; 8 double a[maxn]; 9 10 int main() 11 { 12 a[0] = 0; 13 for(int i = 1; i <= maxn; i++) { 14 a[i] = a[i - 1] + 1.0/i; 15 } 16 int T; 17 int n; 18 scanf("%d", &T); 19 for(int t = 1; t <= T; t++) { 20 scanf("%d", &n); 21 printf("Case %d: ", t); 22 if(n <= maxn) { 23 printf("%.10lf\n", a[n]); 24 } 25 else { 26 printf("%.10lf\n", log(n) + C + 1.0/(2 * n)); 27 } 28 } 29 return 0; 30 }
最后總結一下使用算術基本定理的心得,使用的時候注意分解,如何使用求得的正因子個數之和,以及所有正因子數之和,來計數。關鍵還是將問題轉換為數學模型。