数论
质数 Primes
质数的性质
数学符号“∣”的意义,如a∣b: ‘|’为整除符号,对于整数a,b(a≠0),若存在整数k,使b=ka,则称a整除b,或b能被a整除,记为a∣b。
d | n => n / d | n
也就是说 a * b = c, c能整除a, 也能整除b. 我们找质数只要遍历一遍较小的a即可
质数定理
在 1~n中有n / lnn个质数
试除法判定质数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include<iostream> using namespace std;
const int N = 110;
bool is_Prime(int x) { if(x < 2) return false; for(int i = 2; i <= x / i; i++) if(!(x % i)) return false; return true; }
int main() { int n; cin >> n; while(n--) { int x; cin >> x; if(is_Prime(x)) cout<< "Yes"; else cout << "No"; cout << endl; } return 0; }
|
试除法分解质因数
从小到大分别从n中除干净某个数
最后如果n不是1, 则说明最后一个质数大于根号n, 再单独输出n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<iostream> using namespace std;
int divied(int x) { for(int i = 2; i <= x / i; i++) { if(x % i == 0) { int t = 0; while(x % i == 0) { x /= i; t ++; } cout << i << ' ' << t << endl; } } if(x != 1) cout << x << ' ' << 1 << endl; return 0; }
int main() { int n; cin >> n; while(n--) { int x; cin >> x; divied(x); cout << endl; } return 0; }
|
筛质数
我们把所有数的倍数全都删掉,剩下的数中间全部都不是 2 ~ p - 1的倍数
朴素代码 O(nlnn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<iostream> using namespace std; const int N = 10e6 + 10; int st[N];
int get_primes(int n) { int ret = 0; for(int i = 2; i <=n; i++) if(!st[i]) for(int j = i + 1; j <= n; j++) { if(j % i == 0) { if(!st[j]) { st[j] = 1; ret ++; } } } return n - ret - 1; }
int main() { int n; cin >> n; cout << get_primes(n); return 0; }
|
优化方案, 我们不需要将每个数的倍数全部删掉,只需要把质数的倍数全部删掉. 减少重复计算量.
埃式筛法 O(n loglogn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include<iostream> using namespace std;
const int N = 10e6+10;
int prime[N]; int cnt; char st[N];
int get_prime(int n) { for(int i = 2; i <= n; i++) { if(st[i] == 0) { prime[++cnt] = i; for(int j = 2 * i; j <= n; j += i) st[j] = 1; } } return cnt; }
int main() { int n; cin >> n; cout << get_prime(n); return 0; }
|
线性筛法
n 只会被它的最小质因子删掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<iostream> using namespace std; const int N = 10e6 + 10; int st[N]; int primes[N]; int cnt;
int 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] = 1; if(primes[j] % i == 0) break; } } return cnt; }
int main() { int n; cin >> n; cout << get_primes(n); return 0; }
|
当 $ n >= 10^{7}$ 时 线性筛法比埃式筛法快1倍
约数
int范围内的整数, 约数最多的个数为1500
试除法求约数 $O(\sqrt{n})$
遍历$ [1 , \sqrt{n}]$中所有的数,
如果能被$n$整除添加到答案中
特判$ n = i * i $的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<iostream> #include<vector> #include<algorithm> using namespace std;
const int N = 110;
vector<int> get_divisors(int n) { vector<int> ret; for(int i = 1; i <= n / i; i++) { if(n % i == 0) { ret.push_back(i); if(i != n / i) ret.push_back(n / i); } } sort(ret.begin(),ret.end()); return ret; }
int main() { int m; cin >> m; while(m--) { int n; cin >> n; vector<int> ret = get_divisors(n); for(auto &p : ret) cout << p << ' '; cout << endl; } return 0; }
|
约数个数和约数之和
数学原理: 质因子分解

约数个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<iostream> #include<unordered_map>
using namespace std;
const int mod = 1e9 + 7;
int main() { int n; cin >> n; unordered_map<int,int> prime; while(n--) { int x; cin >> x; for(int i = 2; i <= x / i; i++) { while(x % i == 0) { x /= i; prime[i]++; } } if(x > 1) prime[x]++; } long long ret = 1; for(auto &p:prime) ret = ret * (p.second + 1) % mod; cout << ret; return 0; }
|
约数和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include<iostream> #include<unordered_map>
using namespace std;
const int mod = 1e9 + 7;
int main() { int n; cin >> n; unordered_map<int,int> prime; while(n--) { int x; cin >> x; for(int i = 2; i <= x / i; i++) { while(x % i == 0) { x /= i; prime[i]++; } } if(x > 1) prime[x]++; } long long ret = 1; for(auto &p:prime) { int a = p.first, b = p.second; long long t = 1; while(b--) { t = (t * a + 1) % mod; } ret = ret * t % mod; } cout << ret; return 0; }
|
秦九昭算法 计算多项式之和
最大公约数 (欧几里得算法,辗转相除法)
算法原理
$d | a$ $d|b$ $d| ax + by$
$\gcd(a.b) = \gcd(b,a\mod b)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #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 x, y; cin >> x >> y; cout << gcd(x,y) << endl; } return 0; }
|