boolcheck(int x) { if (x < 2) returnfalse; for (int i = 2; i <= x / i; i ++) if (x % i == 0) returnfalse; returntrue; }
O(nloglogn) 埃拉托尼斯筛法
1 2 3 4 5 6 7 8 9 10 11 12
bool vis[N]; int primes[N], cnt;
voidEratosthenes(int N) { for (int i = 2; i <= N; i ++) { if (!vis[i]) primes[++ cnt] = i; elsefor (int j = i << 1; j <= N; j += i) vis[j] = true; } }
O(n) 欧拉筛法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
bool vis[N]; int primes[N], cnt;
voidEuler_Prime(int N) { for (int i = 2; i <= N; i ++) { if (!vis[i]) primes[++ cnt] = i; for (int j = 1; i * primes[j] <= N; j ++) { vis[i * primes[j]] = true; if (i % primes[j] == 0) break; } } }
因数 / 约数
其中 max{ω(n)} 表示 n 以内不同质因子的最大个数,max{d(n)} 表示 n 以内最大的约数个数。
O(n) 求出所有约数
1 2 3 4 5 6 7 8 9 10 11 12 13
int divs[N], cnt;
inlinevoiddivide(int x) { for (int i = 2; i <= x / i; i ++) { if (x % i == 0) { divs[++ cnt] = i; if (i != x / i) divs[++ cnt] = x / i; } } }
O(n) 求出所有质因数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
int divs[N], cnt;
inlinevoiddivide(int x) { int temp = x; // 不能直接使用 x for (int i = 2; i <= x / i; i ++) { if (temp % i == 0) { while (temp % i == 0) temp /= i; divs[++ cnt] = i; } } if (temp > 1) divs[++ cnt] = temp; }
通过预处理出范围内的所有质数可以进一步优化至 O(p(n)),其中 p(n) 为 n 范围内的所有质数数量,数量级上限大约为 lnnn,大约优化了 lnn 的常数时间,约等于没有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
int primes[N], tot; int divs[N], cnt;
inlinevoiddivide(int x) { int temp = x; for (int i = 1; primes[i] <= x / primes[i]; i ++) { if (temp % primes[i] == 0) { while (temp % primes[i] == 0) temp /= primes[i]; divs[++ cnt] = primes[i]; } } if (temp > 1) divs[++ cnt] = temp; }
欧拉函数
欧拉函数 φ(n) 表示小于等于 n 的与 n 互质的数的个数。特别的,素数的欧拉函数 φ(p)=p−1。
inlinevoiddivide(int x) { int temp = x, res = x; for (int i = 2; i <= x / i; i ++) { if (temp % i == 0) { while (temp % i == 0) temp /= i; res = res / i * (i - 1); } } if (temp > 1) res = res / temp * (temp - 1); return res; }
欧几里得算法
O(logmax(a,b)) 求 gcd(a,b)
1
intgcd(int a, int b){ return a == 0 ? b : gcd(b, a % b); }
1 2 3 4 5
intgcd(int a, int b) { if (a == 0) return b; elsereturngcd(b, a % b); }
intexgcd(int a, int b, int &x, int &y) { if (b == 0) { x = 1, y = 0; return a; } int gcd = exgcd(b, a % b, y, x); y -= a / b * x; return gcd; } // 返回值为 gcd(a, b)
具体做法证明暂时忽略。
快速幂
O(n) 正常做法
1 2 3
for (int i = 1; i <= k; i ++) res = res * base % mod; return res;
O(logn) 位运算快速幂
1 2 3 4 5 6 7 8 9
inlineintksm(int base, int k, constint mod = ::mod) { int res = 1; while (k) { if (k & 1) res = res * base % mod; base = base * base % mod, k >>= 1; } }
逆元
扩欧求逆元
所求的显然就是 ax≡1(modb) 中的 x,即 amodb 的逆元,记为 a−1。
1 2 3 4 5 6 7
voidexgcd(int a, int b, int &x, int &y) { if (b == 0) return x = 1, y = 0, void(); exgcd(b, a % b, y, x); y -= a / b * x; }
费马小定理求逆元
1
inlineintgetinv(int x, int mod){ returnksm(x, mod - 2, mod); }
两者的时间复杂度均为 O(logb)。
O(n) 求阶乘逆元
1 2 3 4 5 6 7 8 9 10
int fc[N], inv[N];
voidInit() { for (int i = fc[0] = 1; i < N; i ++) fc[i] = fc[i - 1] * i % mod; inv[N - 1] = ksm(fc[N - 1], mod - 2); for (int i = N - 2; i >= 0; i --) inv[i] = inv[i + 1] * (i + 1) % mod; }
inlineintksm(int base, int k, constint mod = ::mod) { int res = 1; while (k) { if (k & 1) res = res * base % mod; base = base * base % mod, k >>= 1; } }
int fc[N], inv[N];
inlineintA(int n, int m){ return n < m ? 0 : inv[n] * inv[n - m] % mod; }
inlineintC(int n, int m){ return n < m ? 0 : inv[n] * inv[n - m] % mod * inv[m] % mod; }
voidInit() { for (int i = fc[0] = 1; i < N; i ++) fc[i] = fc[i - 1] * i % mod; inv[N - 1] = ksm(fc[N - 1], mod - 2); for (int i = N - 2; i >= 0; i --) inv[i] = inv[i + 1] * (i + 1) % mod; }
1 2 3 4 5 6 7 8 9 10 11
int C[N][N];
voidInit() { for (int i = 0; i < N; i ++) { C[i][0] = C[i][i] = 1; for (int j = 1; j < i; j ++) C[i][j] = (C[i - 1][j - 1] + C[i][j - 1]) % mod; } }