算術基本定理解析及其應用


摘要

  本文主要講述了算術基本定理的內容,具體的應用形式,重點結合例題展示如何使用算術基本定理求解問題。


 

算術基本定理

  算術基本定理可表述為:任何一個大於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 }
View Code

例題解析

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)i = p1^x1,則 j = p1^m(m屬於[0, x1]),  這樣(i, j)共有  (x1 + 1)種
  (2)j = p1^x1,則 i = p1^n(n 屬於[0, x1]),   這樣(i, j)共有  (x1 + 1)種
那么當 n = p1^x1 時答案就是2 * (x1 + 1)對(i, j),但是當m == n時這一對是計算重復的,所以需要 -1,則最終答案是2 * (x1 + 1) - 1 = 2 * x1 + 1;
推廣至n = p1^x1 * p2^x2 * p3^x3...ps^xs(其中s是唯一分解式中所有質因子的個數)可得總的方案數等於ans = (2 * x1 + 1) * (2 * x2 + 1) * (2 * x3 + 1) ...(2 * xs + 1);
題目中要求的是i <= j,所以需要將總的數目除以2,又由於當i 、j都等於n時只計算了一次,所以除以二之后需要再加上。故最終答案是 ans = ans/2 + 1;
代碼如下
 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 }

lightOJ Harmonic Number

題意

給出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 }

  最后總結一下使用算術基本定理的心得,使用的時候注意分解,如何使用求得的正因子個數之和,以及所有正因子數之和,來計數。關鍵還是將問題轉換為數學模型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM