【数论】【算法】质数及相关
定义
若一个正整数无法被除了 \(1\) 和他自身之外的任何正整数整除,则该数为质数,否则为合数。
判定
\(O(n)\)
bool isprime (int x) {
for (int i = 2; i*i <= n; ++ i) if (!(x%i)) return 0;
reutrn 1;
}
由于正整数的因数对于它的平方根对称(意会一下,一个小于 \(\sqrt n\) 的数必须乘上一个大于 \(\sqrt n\) 的数才能等于 \(n\)),所以只需要对小于 \(\sqrt n\) 的数进行判断。
筛取
找 \(1\sim n\) 范围内的质数。
- 埃式筛
时间复杂度 \(O(n \log \log n)\)。
(其实 \(\log \log 10^9 < 5\),已经是常数级别。)
点击查看代码
int n;
bool inp[man];
vector<int> prime;
int main () {
scanf("%d", &n);
inp[0] = inp[1] = 1;
prime.push_back(2);
for (int i = 3; i <= n; i += 2) {
if (!inp[i]) {
prime.push_back(i);
for (int j = i*i; j <= n; j += i) inp[j] = 1;
}
}
for (int x : prime) printf("%d ", x);
return 0;
}
中间部分 i - j
循环:这是从 i*i
开始的。是因为 2*i
、3*i
……(i-1)*i
在之前已经被 2
、3
……i-1
筛过。
显然除了 \(2\) 以外的偶数都是合数。
(若不需要 prime
数组,则可以令 i <= sqrt(n)
。)
- 欧拉筛(线性筛,xxs)
在埃式筛中虽然已经进行了优化,但是例如 \(12\) 这样的数还是会被 \(2 \times 6\) 和 \(3 \times 4\) 筛两遍。
对于一个数进行质因数分解,即 \(12 = 2 \times 2 \times 3\),且令其只被他的最小质因数筛取,即 \(12\) 只被 \(2 \times 6\) 筛。
点击查看代码
int n;
int mpf[man];
bool inp[man];
vector<int> prime;
int main () {
scanf("%d\n", &n);
inp[0] = inp[1] = 1;
for (int i = 2; i <= n; ++ i) {
if (!inp[i])
prime.push_back(i), mpf[i] = i;
for (int pri_j : prime) {
if (i*pri_j > n) break;
inp[i*pri_j] = 1, mpf[i*pri_j] = pri_j;
if (i%pri_j) break;
}
}
for (int x : prime) printf("%d ", x);
return 0;
}
中间部分 i - pri_j
循环 i % pri_j
判断语句:i
之前已被 pri_j
筛过了。
由于 pri
里面质数是从小到大的,所以 i
乘上其他的质数的结果一定也已经被 pri_j
的倍数筛掉,就不需要在这里筛,直接结束即可。
Extra
注意到欧拉筛求素数的同时也得到了每个数的最小质因数在 mpf
中。
质因数分解
-
试除法
若要分解正整数 \(N\)。
从小到大判定 \(2 \sim \lfloor \sqrt{N} \rfloor\) 中的每个数 \(d\),若 \(d\) 能整除 \(N\), 则从 \(N\) 中除去所有的 \(d\),同时记录。
若 \(2 \sim \lfloor \sqrt{N} \rfloor\) 中没有可以整除 \(N\) 的书,则 \(N\) 为质数(见判定)。
应用
本题的数据范围过于大,\(2147483647\) 是无法在 \(O(n)\) 时间内检测所有质数的。
可以观察到,\(L\sim R\) 的数据范围较小,所以可以每次查询 \(L\sim R\) 内的所有质数,需限制在 \(O(n)\) 内。
又 \(\sqrt{2147483647} \approx 46341\),所以只要找到所有不大于 \(46341\) 的质数再对 \(L\sim R\) 中每个数进行判定即可,在判定时找到距离最大和最小的质数。
单次复杂度 \(O(R-L)\)
点击查看代码
/*
compiling in
ide
g++ test.cpp -o test && ./test
*/
#include <bits/stdc++.h>
#include <bits/extc++.h>
namespace {
#define filein(x) freopen(x".in", "r", stdin)
#define fileout(x) freopen(x".out", "w", stdout)
#define file(x) filein(x), fileout(x)
using namespace std;
using namespace __gnu_pbds;
#define ll long long
#define db double
#define un unsigned
#define ui un int
#define ull un ll
#define udb un db
// template <typename T>
// #define pair<T> pair<T, T>
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pdb pair<db, db>
#define mp(x, y) make_pair(x, y)
const int man = 1e6+10;
}
ui l, r, n = sqrt(2147483647);
ll res;
ui v[man], mpf[man];
vector <ui> pri;
vector <pair<ui, ui> > pril;
int main () {
// file("test");
for (ll i = 2; i <= n; ++ i) {
if (!v[i]) mpf[i] = i, pri.push_back(i);
for (ll j : pri) {
if (i*j > n) break;
v[i*j] = 1;
if (!(i%j)) break;
} }
while (scanf("%u%u", &l, &r) != EOF) {
int minn = 1e6+10, maxx = 0;
pii mi, ma;
memset(v, 0, sizeof(v));
pril.clear();
for (int i = l; i <= r; ++ i) {
for (int j : pri) if ((!(i%j) && i!=j) || i==1) {
v[i-l] = 1;
break;
}
if (!v[i-l])
if (pril.size() >= 1) {
int la = pril.back().first;
if (minn > i-la)
minn = i-la, mi = mp(la, i);
if (maxx < i-la)
maxx = i-la, ma = mp(la, i);
pril.push_back(mp(i, i-la));
}
else pril.push_back(mp(i, 0));
}
if (pril.size() >= 2)
printf("%d,%d are closest, %d,%d are most distant.\n", mi.first, mi.second, ma.first, ma.second);
else puts("There are no adjacent primes.");
} return 0;
}
// ---