文章目录
- 前言
- 一、质数
- 1.质数的判定-试除法
- 2.分解质因子-试除法
- 3.筛选质数
- 二、约数
- 1.求约数-试除法
- 2.约数的个数
- 3.约数之和
- 4.最大公约数-欧几里得算法
前言
本章博客将介绍质数和约数的常用模板,这些题目都比较简单,都可以通过暴力获取答案,但是时间复杂度较高,不符合算法比赛的要求,所以本篇博客介绍的方法都是时间复杂度低效率高的方法,并给出简化复杂度的思路。
质数部分将介绍:质数的判定,分解质因子,筛选质数
约数部分将介绍:求约数,约数的个数,约数之和,最大公约数
一、质数
在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者素数
1.质数的判定-试除法
- 约数成对出现,如果d|n,则n/d必然也整除n,所以我们枚举的时候只需要枚举前一部分即可,也就是d<=r/d这一部分
- 不写成d*d<=r是因为防止爆int
- 不写成d<=sqrt®是因为sqrt效率低
- 时间复杂度从O(n)降低到了O(sqrt(n))
bool is_prime(int n)
{if(n < 2) return false;for(int i = 2; i <= n / i; i ++ )if(n % i == 0)return false;return true;
}
2.分解质因子-试除法
- N = P1a1 * P2a2 * P3a3 * …… * Pkak(P1到k均为质数)
- 从小到大依次枚举,这个不需要考虑是否是质数,因为当枚举到i且成立时,就已经说明i不能被2~i-1整除,说明i已经是质数
- n最多只有一个大于sqrt(n)的数,因为如果有两个大于,则两数相乘大于n不符合逻辑,所以枚举只需要枚举到i<=n/i,最后剩余的一个不是1就是大于sqrt(n)的最后一个质因子
- 时间复杂度从O(n)降低到了介于O(logn)~O(sqrt(n))
void divide(int n)
{for(int i = 2; i <= n / i; i ++ )if(n % i == 0){int s = 0; //次数 while(n % i == 0){n /= i;s ++;}cout << i << ' ' << s << endl;}if(n > 1) cout << n << ' ' << 1 << endl;puts(""); //换行
}
3.筛选质数
- 埃氏筛法
- 从小到大枚举质数(不用枚举所有数),每次删除质数的倍数
- 时间复杂度从O(nlogn)降低为O(nloglogn)
const int N = 1000010;int primes[N], cnt;
bool st[N]; //判断是否是合数void get_primes(int n)
{for(int i = 2; i <= n; i ++ ){if(!st[i]){primes[cnt++] = i;for(int j = i + i; j <= n; j += i) st[j] = true;}}
}
- 线性筛法
- i%pj == 0:由于从小向大枚举,pj一定是i的最小质因子,pj一定是pj * i的最小质因子
- i%pj != 0:由于从小向大枚举,pj一定小于i的所有质因子,pj也一定是pj * i的最小质因子
- 所以在i%pj == 0时break即可,后面的都不满足了,但是从开始枚举到此的全都满足pj一定是pj * i的最小质因子
- 时间复杂度比埃氏更低
const int N = 1000010;int primes[N], cnt;
bool st[N];void get_primes(int n)
{for(int i = 2; i <= n; i ++ ){if(!st[i]) primes[cnt ++ ] = i;for(int j = 0; primes[j] <= n / i; j ++ ){st[primes[j] * i] = true;if(i % prime[j] == 0) break;}}
}
二、约数
约数,又称因数。整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。
1.求约数-试除法
- 约数成对出现,如果d|n,则n/d必然也整除n,所以我们枚举的时候只需要枚举前一部分即可,也就是d<=r/d这一部分
vector<int> get_divisors(int n)
{vector<int> res; //存n的所有约数for(int i = 1; i <= n / i; i ++ ){res.push_back(i);if(i != n / i) res.push_back(n / i);}sort(res.begin(), res.end());return res;
}
2.约数的个数
- N = P1a1 * P2a2 * P3a3 * …… * Pkak
- 约数个数 = (a1+1)*(a2+1)*(a3+1)*……*(ak+1)
- 运用前面分解质因子-试除法
/*题目:给定 n 个正整数 ai,请你输出这些数的乘积的约数个数,答案对 109+7 取模。*/
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>using namespace std;typedef long long LL;const int N = 110, mod = 1e9 + 7;int main()
{int n;cin >> n;unordered_map<int, int> primes;while (n -- ){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 p : primes) res = res * (p.second + 1) % mod;cout << res << endl;return 0;
}
3.约数之和
- N = P1a1 * P2a2 * P3a3 * …… * Pkak
- 约数之和=(P10+P11+……+P1a1)*……*(Pk0+Pk1+……+Pkak)
- 运用前面分解质因子-试除法
/*题目:给定 n 个正整数 ai,请你输出这些数的乘积的约数之和,答案对 109+7 取模。*/
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>using namespace std;typedef long long LL;const int N = 110, mod = 1e9 + 7;int main()
{int n;cin >> n;unordered_map<int, int> primes;while (n -- ){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 p : primes){LL a = p.first, b = p.second;LL t = 1;while (b -- ) t = (t * a + 1) % mod; //求公式的每个括号项的技巧res = res * t % mod;}cout << res << endl;return 0;
}
4.最大公约数-欧几里得算法
- 核心:辗转相除
- d|a且d|b可以推得:d|a*x+b*y
- 由上式可得:(a, b)最大公约数 =(b, a mod b)
int gcd(int a, int b)
{return b ? gcd(b, a % b) : a;
}