算法刷题-数论-试除法求约数、约数个数、约数之和、最大公约数(辗转相除法)
文章目錄
- acwing869. 試除法求約數(shù)
- acwing870. 約數(shù)個(gè)數(shù)
- acwing871. 約數(shù)之和
- acwing872. 最大公約數(shù)
acwing869. 試除法求約數(shù)
acwing869. 試除法求約數(shù)
試除法求一個(gè)數(shù)的所有約數(shù):從小到大枚舉,如果當(dāng)前枚舉的數(shù)是原數(shù)的約數(shù),就記錄下來(或者輸出)。
優(yōu)化:如果 d能整除n的話,那么 n/d也能整除n;比如,3能整除6,那么 6/3=2,2也能整除6.因?yàn)榧s數(shù)是一對(duì)一對(duì)出現(xiàn)的。
這里的的優(yōu)化是只枚舉較小的約數(shù)d,另一個(gè)可以直接n/d算出來,這樣的話只需要枚舉到n\sqrt{n}n?即可。
試除法求一個(gè)數(shù)所有約數(shù),時(shí)間復(fù)雜度O(n)O(\sqrt{n})O(n?)
ac代碼
#include<bits/stdc++.h> using namespace std;//用vector來存所有約數(shù) vector<int> get_divisors(int n){vector<int> res;for(int i= 1; i<= n/ i; i++){if(n %i ==0){res.push_back(i);//邊界情況,i == n/i,只放一個(gè)就可以if(i != n/i) res.push_back(n/i);}}sort(res.begin(),res.end());return res; } int main(){int n;cin >> n;while(n--){int a;cin >> a;vector<int > res = get_divisors(a);//下面這種寫法,避免行末空格,在PAT這種OJ上會(huì)卡掉!cout<<res[0];for(int i;i<res.size(); i++) cout<<" "<<res[i];cout<<endl;}}acwing870. 約數(shù)個(gè)數(shù)
acwing870. 約數(shù)個(gè)數(shù)
分析:
約數(shù)個(gè)數(shù)基于算術(shù)基本定理
來源于:維基百科
假設(shè)N分解質(zhì)因數(shù)(下面的p1,p2,...,pkp_1,p_2,...,p_kp1?,p2?,...,pk?都是質(zhì)數(shù))的結(jié)果是:
N=p1a1×p2a2×...×pkakN={p_1}^{a_1}\times{p_2}^{a_2}\times...\times{p_k}^{a_k}N=p1?a1?×p2?a2?×...×pk?ak?
則N的約數(shù)個(gè)數(shù)就等于(a1+1)×(a2+1)×...×(ak+1)(a_1+1)\times(a_2+1)\times...\times(a_k+1)(a1?+1)×(a2?+1)×...×(ak?+1)
所以這道題,最后的那個(gè)數(shù)N是給定的所有數(shù)的乘積,對(duì)N進(jìn)行分解因式,其實(shí),可以對(duì)乘起來之前的每一個(gè)數(shù)分解質(zhì)因數(shù),相同的因數(shù),次冪累加起來。
用到分解質(zhì)因數(shù)的知識(shí),請(qǐng)移步筆者另一篇文章:
算法刷題-數(shù)論-質(zhì)數(shù)的判定、分解質(zhì)因數(shù)、篩質(zhì)數(shù)中分解質(zhì)因數(shù)的知識(shí)點(diǎn)。
ac代碼
#include<bits/stdc++.h> using namespace std;const int mod =1e9 + 7; typedef long long LL;int main(){int n;cin >> n;unordered_map<int,int> primes;//質(zhì)因數(shù)的次冪while(n--){//每個(gè)數(shù)都分解質(zhì)因數(shù),并統(tǒng)計(jì)因數(shù)的次冪int x;cin >> x;for(int i=2; i<= x/i ;i++){while( x % i ==0 ){x/=i;primes[i] ++;}}if(x >1)primes[x] ++;}LL res =1;//約數(shù)個(gè)數(shù):套公式(1+a1)(1+a2)...(1+ak)for(auto prime : primes) res = res*(prime.second+1) % mod;cout<<res<<endl;}acwing871. 約數(shù)之和
acwing871. 約數(shù)之和
分析:
假設(shè)N分解質(zhì)因數(shù)的結(jié)果是:
N=p1a1×p2a2×...×pkakN={p_1}^{a_1}\times{p_2}^{a_2}\times...\times{p_k}^{a_k}N=p1?a1?×p2?a2?×...×pk?ak?
則這個(gè)數(shù)N的約數(shù)之和等于
(p10+p11+p12+...+p1a1)×(p20+p21+p22+...+p2a2)×...×(pk0+pk1+pk2+...+pkak)(p_{1}^{0}+p_{1}^{1}+p_{1}^{2}+...+p_{1}^{a_{1}})\times(p_{2}^{0}+p_{2}^{1}+p_{2}^{2}+...+p_{2}^{a_{2}})\times...\times(p_{k}^{0}+p_{k}^{1}+p_{k}^{2}+...+p_{k}^{a_{k}})(p10?+p11?+p12?+...+p1a1??)×(p20?+p21?+p22?+...+p2a2??)×...×(pk0?+pk1?+pk2?+...+pkak??)
以上稱為“約數(shù)和定理”,在小學(xué)奧數(shù)中有應(yīng)用。
補(bǔ)充:已知pap^apa的底數(shù)p,指數(shù)a,如何快速求出p0+p1+...+pap^0+ p^1+...+p^ap0+p1+...+pa?
有以下代碼
//求(p^0+ p^1+...+p^a)的代碼 int t =1; while(a--) t = t* p +1;ac代碼
#include<bits/stdc++.h> using namespace std;const int mod =1e9 + 7; typedef long long LL;int main(){int n;cin >> n;unordered_map<int,int> primes;//質(zhì)因數(shù)的次冪while(n--){//每個(gè)數(shù)都分解質(zhì)因數(shù),并統(tǒng)計(jì)因數(shù)的次冪int x;cin >> x;for(int i=2; i<= x/i ;i++){while( x % i ==0 ){x/=i;primes[i] ++;}}if(x >1)primes[x] ++;}LL res =1;for(auto prime : primes){int p = prime.first , a = prime.second; //分別是底數(shù)和指數(shù)LL t =1;//對(duì)每個(gè)質(zhì)因子,求(p^0+ p^1+...+p^k)while(a--) t = (t * p +1) %mod;res = res * t % mod;}cout<<res<<endl;}acwing872. 最大公約數(shù)
acwing872. 最大公約數(shù)
輾轉(zhuǎn)相除法的核心:
(a,b)的最大公約數(shù) =(b,a mod b)的最大公約數(shù)
amodb=a??ab?b=a?kb,k為常數(shù)a\ mod \ b = a-\lfloor \frac{a}{b}\rfloor b =a - kb,k為常數(shù)a?mod?b=a??ba??b=a?kb,k為常數(shù)
怎么證明呢?思想是證明(a,b)的公約數(shù) 和(b, a mod b)的公約數(shù)完全相同,則它們的最大公約數(shù)也相同。等價(jià)表述是,任何1個(gè)(a,b)的公約數(shù),都是(b,a mod b)的公約數(shù);反過來,任何1個(gè)(b,a mod b)的公約數(shù),都是(a,b)的公約數(shù),那么(a,b)的公約數(shù) 和(b, a mod b)的公約數(shù)完全相同。
具體證明過程略,提示:會(huì)用到數(shù)論里面的結(jié)論,若d能整除a,d能整除b,則d整除k1a+k2b,k1,k2k_1a+k_2b,k_1,k_2k1?a+k2?b,k1?,k2?是常數(shù)。
輾轉(zhuǎn)相除法模板解釋:
int gcd(int a, int b){//如果b !=0 則返回 gcd(b, a mod b)//如果 b==0,則返回 a ,因?yàn)?和a的最大公約數(shù)areturn b ? gcd(b, a %b) :a; }AC代碼
#include<bits/stdc++.h> using namespace std;int gcd(int a, int b){return b ? gcd(b, a %b) :a; } int main(){int n;cin >> n;while(n--){int a, b;scanf("%d%d",&a,&b);printf("%d\n",gcd(a,b));}}總結(jié)
以上是生活随笔為你收集整理的算法刷题-数论-试除法求约数、约数个数、约数之和、最大公约数(辗转相除法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAT甲级1059 Prime Fact
- 下一篇: PAT甲级1081 Rational S