質數與約數:
整除與約數
設 \(n\) 為非負整數,\(d\) 為正整數,若 \(\frac{n}{d}\) 為整數,則稱 \(d\) 整除 \(n\),記為\(d\mid n\)。此時,則稱 \(d\) 是 \(n\) 的約數,或因數、因子;稱 \(n\) 為 \(d\) 的倍數。
質數與合數的定義:
對於 \(n \geq 2\) ,若 \(\forall 1<i<n,i\nmid n\) ,則稱 \(n\) 為質數,否則 \(n\) 為合數。
質數判定:
試除法:
單次時間複雜度 \(O(\sqrt n\ )\)
程式碼
bool is_prime(int n) {
if(n < 2)
return false;
for(int i=2; i<= sqrt(n); ++i) {
if(n % i == 0)
return false;
}
return true;
}
質數篩法一:
從 \(2\) 到 \(n\) 列舉整數 \(i\),標記大於 \(i\) 且不大於 \(n\) 的 \(i\) 的倍數。列舉到 \(i\) 時,若 \(i\) 沒有被標記過,則 \(i\) 為質數。
時間複雜度 \(O(\sum_{i=1}^n \frac{n}{i})=O(n\log n)\)
程式碼
void found_prime() {
memset(vis, 0, sizeof(vis));
vis[0] = 1; vis[1] = 1; // 特殊處理 0 和 1
for(int i=2; i<=n; ++i) {
for(int j=i*2; j<=n; j+=i)
vis[j] = 1; // 標記合數
}
}
埃氏篩:
只需要列舉質數的倍數就可以標記到所有合數了。實際上對於每個質數 \(p\) 而言,小於\(p^2\) 的 \(p\) 的倍數在掃描到更小的質數時就已經被標記過了。
從 \(2\) 到 \(n\) 列舉整數 \(i\),若 \(i\) 是質數,則把 \(i^2\) , \((i+1) \times i\), \(\cdots\), \(\lfloor \frac{n}{i}\rfloor \times i\) 標記為合數。列舉到 \(i\) 時,若 \(i\) 沒有被標記過,則 \(i\) 為質數。
時間複雜度 \(O(\sum_{pr[i]\leq n}\frac{n}{pr[i]})=O(n\log\log n)\)
程式碼
void found_prime() {
memset(vis, 0, sizeof(vis));
vis[0] = 1; vis[1] = 1; // 特殊處理 0 和 1
for(int i=2; i<=n; ++i) {
if(!vis[i]) { // i 為質數
for(int j=i*i; j<=n; j+=i)
vis[j] = 1; // 標記合數
}
}
}
線性篩:
用每一個合數的最小質因子來標記這個合數。時間複雜度 \(O(n)\)
程式碼
int pr[N],cnt;
bool vis[N];
void init(){
int n=1e5+50;
for(int i=2;i<=n;i++){
if(!vis[i]){pr[++cnt]=i;}
for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0)break;
}
}
}
區間篩:(埃氏篩)
程式碼
const int maxn = 1e6+10;
typedef long long LL;
bool is_prime[maxn]; //標記a~b範圍內的質數
bool is_prime_small[maxn]; //標記 1~sqrt(n)範圍內的所有質數
LL a, b;
//對區間[a, b]內的整數執行篩法,is_prime[i-a]=true表示i是素數
void found_prime()
{
for(LL i=0; i*i<=b; ++i)
is_prime_small[i] = true;
is_prime_small[1] = false;
for(LL i=0; i<=b-a; ++i)
is_prime[i] = true;
for(LL i=2; i*i<=b; ++i) {
if(is_prime_small[i]) { // i是質數
for(LL j=i*i; j*j<=b; j+=i) // 標記 sqrt(b) 以內的i的倍數
is_prime_small[j] = false;
for(LL j = max(2LL, (a+i-1)/i) * i; j<=b; j+=i) //標記[a, b]中i的倍數
is_prime[j-a] = false;
}
}
}
算術基本定理:
任何一個大於 1 的正整數都能唯一分解為若干個質數的乘積。
設 \(n\geq2\) 為整數,有唯一的分解式:
其中 \(c_i\) 都是正整數,\(p_i\) 都是質數,且滿足 \(p_1\leq p_2\leq...\leq p_m\)
根據算術基本的定理,對於任意一個大於 \(2\) 的整數 \(n\),他的正約數集合可以寫作:
推論一: \(n\) 的正約數個數為:
推論二: \(n\) 的所有正約數之和為:
階乘分解:
對於一個數 \(n\) 求 \(n!\) 的質因數分解:
考慮對於一個質數 \(p_i\) 求 \(n!\) 中包含多少個 \(p_i\)
答案是 \(\sum_{i=1}^{\lfloor\log_pn\rfloor}\lfloor\frac{n}{p^i}\rfloor\)
對於每一個質數都用如上方式
共有 \(\frac{n}{\ln n}\) 個數,每次處理 \(\log n\) 的複雜度,總複雜度 \(O(n)\) 。
求解正約數集合:
試除法 求 \(n\) 的正約數:
時間複雜度 \(O(\sqrt n )\)
程式碼
int divisor[10010], cnt = 0;
for(int i=1; i<=sqrt(n); ++i) {
if(n % i == 0) {
divisor[++cnt] = i;
if(i != n/i) divisor[++cnt] = n/i;
}
}
推論:一個整數 \(n\)的正約數個數最多不超過 \(2\times \sqrt n\) 個
倍數法 求\(1\sim n\)的正約數:
先列舉 \(1 \sim n\) 中的每一個數作為約數 \(d\),再在 \(1\sim n\) 尋找 \(d\) 的倍數即可。時間複雜度 \(O(n+\frac{n}{2}+\cdots+\frac{n}{n})=O(n\log n)\)
程式碼
vecotr<int> divisor[500010];
for(int i=1; i<=n; ++i) { // 先列舉約數 i
for(int j=1; j<=n/i; ++j) //列舉 i 在 1~n 範圍內的倍數 i × j
divisor[i*j].push_back(i); // i 是 i × j 的約數
}
推論:\(1\sim n\) 的約數個數總和大約為 \(n\log n\) 個。
約數研究:
設 \(f(x)\) 為 \(x\) 的約數個數,求 \(\sum_{i=1}^nf(i)\)
可以列舉每一個 \(i\in[1,n]\) ,\(1\sim n\) 中含有 \(\lfloor\frac{n}{i}\rfloor\) 個約數 \(i\) 。所以答案為 \(\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\)
\({\rm gcd}\) :
\(\forall a,b\in \mathbb{N},d\in \mathbb{N^{*}}\) 若 \(d\mid a\ \&\ d\mid b\) 則稱 \(d\) 為 \(a,b\) 的公約數。
\(a,b\) 的公約數中最大的稱為 \(a,b\) 的最大公約數,記為 \(\gcd(a,b)\)。
性質:
- \(\gcd(a,b)=\gcd(b,a)\)
- 對於 $\forall a,b\in \mathbb{N} $ 若 \({\rm gcd}(a,b)=1\) 則稱 \(a,b\) 互質。
- 由於任何正整數都是 \(0\) 和 \(0\) 的公約數,故 \(\gcd (0,0)\) 不存在。
- 對於 \(\forall a\in \mathbb{N^{*}}\) ,有 \(\gcd(a,a)=a,\gcd(a,0)=a\) 。
- \(\forall k\in \mathbb{Z}\),有 \(\gcd(k\times a,k\times b)=k\times \gcd(a,b)\) 。
更相減損術:
\(\forall a,b\in \mathbb{N^{*}}\ \&\ a\geq b\) 有 \(\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)
\(\forall d\mid a\ \&\ d\mid b\),有 \(a=k_1\times d,b=k_2\times d\) ,則 \((a-b)=(k_1-k_2)\times d\) ,所以 \(d\) 也是 \(b,a-b\) 的公約數,所以 \(\gcd(a,b)=\gcd(b,a-b)\) 。
輾轉相除法:
\(\forall a,b\in \mathbb{N^{*}}\ \&\ b!=0\) 有 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
- 若 \(a<b\) ,則 \(\gcd(b,a\bmod b)=gcd(b,a)=gcd(a,b)\)
- 若 \(a\geq b\) ,設 \(a=q\times b+r\) ,其中 \(0\leq r <b\) 。\(r=a\bmod b\) 。有 \(a=k_1\times d,q\times b=k_2\times d\) ,則 \(r=(a-q\times b)=(k_1-k_2)\times d\) ,因此 \(d\) ,也是 \(b,r\) 的公約數,故 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
程式碼:
程式碼
int gcd(int a, int b) {
while(b > 0 ) {
int x = a % b;
a = b;
b = x;
}
return a;
}
遞迴:
程式碼
int gcd(int a, int b) {
return b ? gcd(b, a%b) : a;
}
最大複雜度 \(O(\log(a+b))\)
\({\rm lcm}\):
\(\forall a,b\in\mathbb{N^{*}},m\in \mathbb{N}\) 若 \(a\mid m\ \&\ b\mid m\),則稱 \(m\) 為 \(a\) 和 \(b\) 的公倍數。
\(a,b\) 的公倍數中最小的稱為 \(a,b\) 的最小公倍數,記為 \({\rm lcm}(a,b)\) 。
實際程式碼中: \({\rm lcm(a,b)=\frac{a}{\gcd(a,b)}\times b}\)
gcd與lcm:
給出某兩個整數 \(a\) 和 \(b\) (\(a\leq b\))的最大公約數 \(GCD\) 和最小公倍數 \(LCM\) ,請找出滿足的 \(a\) 和 \(b\) ,使得 \(b-a\) 的值最小。
\(\because LCM=\frac{a\times b}{GCD}\)
\(\therefore \frac{a\times b}{GCD^2}=\frac{LCM}{GCD} \Rightarrow (\frac{a}{GCD})^2\leq \frac{LCM}{GCD}\)
\(\therefore \frac{a}{GCD}\leq \sqrt{\frac{LCM}{GCD}}\)
設 \(a'=\frac{a}{GCD}\) ,從 \(\sqrt{\frac{LCM}{GCD}}\sim 1\) 來列舉 \(a'\) ,當 \(a'\mid \frac{LCM}{GCD}\&\& \gcd(a',LCM/GCD/a')=1\) 時停止列舉。
答案即為 \(a=a'\times GCD,b=\frac{LCM}{a'}\)
CF1152C:
給定兩個正整數 \(a,b\) ,找到非負整數 \(k\) 使 \(a+k\) 與 \(b+k\) 的最小公倍數最小,如有多解輸出最小的 \(k\) 。
設 \(a>b\) 顯然 \({\rm lcm}(a+k,b+k)=\frac{(a+k)(b+k)}{\gcd(a+k,b+k)}\)
由輾轉相除法可知 \(\gcd(a,b)=\gcd(b,a-b)\)
所以 \({\rm lcm}(a+k,b+k)=\frac{(a+k)(b+k)}{\gcd(b+k,a-b)}\)
所以可以列舉 \(a-b\) 的因子 \(w\) ,若 \(b\%w=0\) 則 \(k=0\) ,否則 \(k=(\lfloor\frac{b}{w}\rfloor+1)w-b\)