质数
定义
在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者叫素数。
相关算法
1.质数的判定——试除法 O(sqrt(n))
概念:
判断每个从2开始到n - 1的数是不是能将x整除,从而来判断x是不是质数。
优化:如果x能够被i整除,那么x也能够被x / i整除。所以我们可以取较小的那部分(x / i)来判断是否为质数。
代码:
#include <iostream>
using namespace std;
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;
}
int main()
{
int x;cin >> x;
is_prime(x);
return 0;
}
2.分解质因数——试除法
概念:
判断每个从2开始到n 的数是不是n的质因子并统计数量。
优化:由于n中最多只包含一个大于sqrt(n)的质因子(如果有两个,两两相乘就会大于n),所以我们可以从2开始遍历sqrt(n),最后剩下的n如果大于1,那么就是大于sqrt(n)的那个质因子了。
代码:
#include <iostream>
using namespace std;
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;;
}
int main()
{
int x;cin >> x;
divide(x);
return 0;
}
3.筛质数
1).埃式筛法
概念:
给定一个数n,求2到n的所有质数。
基本步骤:用i从2开始遍历到n,删掉所有i的倍数的数(不包括自己)。
优化:只需删掉质数的所有倍数(把for的步骤放到判断里)。
i这个数是质数,那么前面就没有数能使其删掉。如果i是合数,那么前面就一定有一个数的倍数是他,不然他就是质数了。
这个优化也被称为埃式筛法 O(nloglogn)
代码:
#include <iostream>
using namespace std;
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;//优化
}
//for (int j = i + i; j <= n; j += i) st[j] = true;//未优化前
}
}
int main()
{
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;
return 0;
}
2). 线性筛法
概念:
核心:n只会被最小质因子筛掉
st[ primes[ j ] * i ] = true; //n只会被最小质因子筛掉
证明:
1. i % pj == 0
pj一定是i的最小质因子,pj一定是pj * i 的最小质因子
2. i % pj != 0
pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子。
合数一定会被删掉
对于一个合数x,假设pj是x的最小质因子,当i枚举到x/pj时,就会被筛掉。
线性概念:每个数只有一个最小质因子,从而每个数只会被筛一次,所以它是线性的。
线性筛法比埃式筛法要快
代码:
#include <iostream>
using namespace std;
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 % primes[j] == 0) break; //primes[j]一定是i的最小质因子
}
}
}
int main()
{
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;
return 0;
}
约数
定义:
整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。
例如:4的约数:1,2,4
相关算法:
1.试除法求约数
概念:
从1到n,判断每个数是否为n的约数
优化:d为n的约数,那么d/n也是n的约数。所以我们可以枚举较小的约数,然后再把较大的也添加进来。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
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也是约数
if (i != n / i) res.push_back(n / i);//判断n是否为i的平方
}
}
sort(res.begin(), res.end());
return res;
}
int main()
{
int x;cin >> x;
vector<int> v = get_divisors(x);
return 0;
}
2.约数个数
概念:
![]()
核心:把n化为质因数相乘。p表示质因数。然后每个xk从0到xk里取值。所以就有总个数
(x1 + 1)(x2 + 1)……(xk + 1)。
代码:
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int 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 prime : primes) res = res * (prime.second + 1) % mod;
cout << res << endl;
return 0;
}
3.约数之和
概念:
![]()
核心:从x1到xk中,只要有一个不同,那么就是不同的两个数。所以我们就可以列出上面的公式。
代码:
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int 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 prime : primes) {
int p = prime.first, a = prime.second;
ll t = 1;
//求(p^0 + p^1 + p^2 … + p^xk)
while (a--) t = (p * t + 1) % mod;//假设a为2,通过计算就可以得到p^2 + p + 1
res = res * t % mod;
}
cout << res << endl;
return 0;
}
4.欧几里得算法(辗转相除法)
概念:
核心:(a,b)= (b,a % b)——a,b的最大公约数就等于b,a%b的最大公约数
证明:假设d能整除a,也能整除b,那么d就可以整除(ax + by)。利用这个可以正向证明。
如果d能够整除a,b。那么d也能整除b,a % b(a - [a/b] * b)。这里可以把a % b当中a - c * b.
反向证明:假设d能够整除b,a - c * b。那么d也可以整除a - c * b + c * b,d可以整除a。所以(a,b)= (b,a % b)的公约数是一样的。故最大公约数也是一样的。
d
代码return b ? gcd(b, a % b) : a;含义就是不断gcd。直到b等于0。(a,0)的最大公约数就是a(因为0可以整除任何数),此时返回a就可以了。
代码:
#include <iostream>
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;
cin >> a >> b;
cout << gcd(a, b) << endl;
}
return 0;
}