vector<int> p, f1, f2(r - l + 1); auto eratosthenes = [&](int maxn) { f1.resize(maxn + 1); for (i64 i = 2; i <= maxn; i++) { if (f1[i]) continue; p.push_back(i); for (i64 j = i * i; j <= maxn; j += i) { if (!f1[j]) f1[j] = i; } for (i64 j = max(i, (l + i - 1) / i) * i; j <= r; j += i) { if (!f2[j - l]) f2[j - l] = i; } } }; eratosthenes(ceil(sqrt(r)) + 1);
欧拉筛
欧拉筛也称线性筛,每个合数只会被标记一次,时间复杂度 $O(n)$ 。
欧拉筛不保证每个一定被合数被最小值质因数标记,因此不可以筛至平方根。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
vector<int> pr, vis; auto eular = [&](int maxn) { vis.resize(maxn + 1); for (i64 i = 2; i <= maxn; i++) { if (!vis[i]) pr.push_back(i); for (auto x: pr) { if (i * x > maxn) break; vis[i * x] = true; if (i % x == 0) break; } } }; eular(n);
auto pi = [&](i64 n) { i64 m = ceil(sqrt(n)); vector<i64> p(m + 1), v(m + 1); for (i64 i = 1; i <= m; i++) p[i] = n / i - 1, v[i] = i - 1; for (i64 i = 2; i <= m; i++) { if (v[i] == v[i - 1]) continue; for (i64 j = 1; j <= min(m - 1, n / i / i); j++) { if (i * j < m) p[j] -= p[i * j] - v[i - 1]; else p[j] -= v[n / i / j] - v[i - 1]; } for (i64 j = m; j >= i * i; j--) v[j] -= v[j / i] - v[i - 1]; } return p[1]; };
boolmiller_rabin(i64 n){ if (n < 3 || n % 2 == 0) return n == 2; if (n % 3 == 0) return n == 3; i64 u = n - 1, t = 0; while (u % 2 == 0) u /= 2, ++t; for (auto a: base) { if (a % n >= -1 && a % n <= 1) continue; i64 v = qpow(a % n, u, n); if (v == 1) continue; i64 s; for (s = 0; s < t; ++s) { if (v == n - 1) break; v = qmul(v, v, n); } if (s == t) returnfalse; } returntrue; }
Pollard-Rho 分解因数
Pollard-Rho 算法是一种随机化算法,可以在 $O(\sqrt p)=O(n^{1/4})$ 的期望复杂度获得一个非平凡因子(非平凡因子不一定是素因子),其中 p 是 n 的最小素因子。
有概率分解失败,退出并返回 n 本身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
i64 pollard_rho(i64 x){ i64 s = 0, t = 0; i64 c = (i64) rnd() % (x - 1) + 1; i64 val = 1, step = 0; for (int goal = 1;; goal *= 2, s = t, val = 1) { for (step = 1; step <= goal; ++step) { t = (qmul(t, t, x) + c) % x; val = qmul(val, abs(t - s), x); if (step % 127 == 0) { i64 d = gcd(val, x); if (d > 1) return d; } } i64 d = gcd(val, x); if (d > 1) return d; } }
intexgcd(int a, int b, int& x, int& y){ if (b == 0) { x = 1, y = 0; return a; } int d = exgcd(b, a % b, x, y), tmp = x; x = y, y = tmp - a / b * y; return d; }
boolliEu(int a, int b, int c, int& x, int& y){ int d = exgcd(a, b, x, y); if (c % d != 0) returnfalse; int k = c / d; x *= k, y *= k; returntrue; }
扩展中国剩余定理
扩展中国剩余定理通过扩展欧几里得算法合并同余方程,时间复杂度 $O(\log{n})$ 。
1 2 3 4 5 6 7 8 9 10
boolexcrt(i64 a1, i64 m1, i64 a2, i64 m2, i64 &a, i64 &m){ i64 x, y, c = a2 - a1; i64 d = exgcd(m1, m2, x, y); if (c % d != 0) returnfalse; i64 t = m2 / d; x = (i128) (x % t + t) % t * (c / d) % t; a = a1 + x * m1, m = m1 / d * m2; a = (a % m + m) % m; returntrue; }
线性求逆元
以下方法可以在 $O(n)$ 线性时间内求解出 1~n 或任意 n 个数的逆元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
vector<int> inv(n + 1);
voidget_invs(int n){ inv[1] = 1; for (int i = 2; i <= n; ++i) { inv[i] = (p - p / i) * inv[p % i] % p; } }
vector<i64> a(n + 1); // 所求任意n个数的列表 voidget_invs(int n){ vector<i64> s(n + 1), sv(n + 1); s[0] = 1; for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p; sv[n] = qpow(s[n], p - 2); for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p; for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p; }
i64 C(int n, int k){ if (k < 0 || k > n) return0; k = min(k, n - k); i64 res = 1; for (int i = 0; i < k; ++i) res = res * (n - i) / (i + 1); return res; }
线性预处理出阶乘及其逆元,可以在 $O(1)$ 的时间复杂度内计算组合数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
constexprint maxa = 100000; // 预处理范围 vector<i64> A(maxa + 1); vector<i64> IA(maxa + 1); vector<i64> inv(maxa + 1); voidinit(){ A[0] = 1, inv[1] = 1, IA[0] = 1; for (int i = 1; i <= maxa; ++i) A[i] = A[i - 1] * i % p; for (int i = 2; i <= maxa; ++i) inv[i] = (p - p / i) * inv[p % i] % p; for (int i = 1; i <= maxa; ++i) IA[i] = IA[i - 1] * inv[i] % p; } i64 C(int n, int k){ if (k < 0 || k > n) return0; return A[n] * IA[k] % p * IA[n - k] % p; }
i64 lucas(i64 n, i64 k, i64 p){ i64 res = 1; while (n > 0 || k > 0) { i64 ni = n % p, ki = k % p; if (ki > ni) return0; res = res * C(ni, ki, p) % p; n /= p, k /= p; } return result; }
i64 eabs(i64 a, i64 b, i64 c, i64 d){ if (a > c) swap(a, c), swap(b, d); if (b > d) { i64 el = eabs(a, d, c, d); i64 pl = qdiv(d - a + 1, b - a + 1); i64 er = eabs(d + 1, b, c, d); i64 pr = qdiv(b - d, b - a + 1); return (el * pl + er * pr) % p; } i64 n = max(b - c + 1, 1ll); i64 em = qdiv(n * n - 1, 3 * n); i64 pm = qdiv(n * n, (b - a + 1) * (d - c + 1)); return (qdiv(c + d - a - b, 2) + em * pm) % p; }
线性代数
异或线性基
构造方法
本方法构造的异或线性基具有如下性质:
线性基没有异或和为 0 的子集。
线性基中各数二进制最高位不同。
如果某一位是一个元素的最高有效位,则其他元素在这一位均为 0 。
1 2 3 4 5 6 7
vector<u64> base; auto insert = [&](u64 x) { for (auto b: base) x = min(x, b ^ x); for (auto &b: base) b = min(b, b ^ x); if (x) base.push_back(x); };
因为 bitset 不提供 < 操作符,可以使用用 base[n] 存最高有效位为 n 的元素。
1 2 3 4 5 6 7 8 9 10 11 12
bitset<N> base[N];
auto insert = [&](bitset<N> x) { for (int i = 0; i < N; ++i) if (x[i]) x ^= base[i]; if (x == 0) return; int msb = N - 1; // 最高有效位 while (msb && x[msb] == 0) msb--; base[msb] = x; for (int i = msb + 1; i < N; ++i) if (base[i][msb]) base[i] ^= x; };
第 k 小异或和
对于某个元素,选后异或和总是比不选异或和更大,所以最大异或和就是整个集合的异或和。
进一步地,排序后还可求出第 k 小异或和,只需按照 k 的二进制分解去选择基数即可获得第 k 小异或和。
1 2 3 4 5 6 7 8 9 10
sort(base.begin(), base.end());
u64 ans = 0; if (base.size() < n) k--; // 线性基可异或出 0 for (auto b : base) { if (k & 1) ans ^= b; k >>= 1; } if (k == 0) cout << ans << endl; else cout << -1 << endl;
template<typename T> structpos { T x, y; pos operator+(const pos &b) const { return {x + b.x, y + b.y}; } pos operator-(const pos &b) const { return {x - b.x, y - b.y}; } T operator^(const pos &b) const { return x * b.y - y * b.x; } T operator*(const pos &b) const { return x * b.x + y * b.y; } pos operator*(const T &b) const { return {x * b, y * b}; } pos operator/(const T &b) const { return {x / b, y / b}; } booloperator==(const pos &b) const { return !sgn(x - b.x) && !sgn(y - b.y); } [[nodiscard]] T abs2()const{ return x * x + y * y; } }; using Pi = pos<i64>; using Pf = pos<f80>; // 获得点 a 到点 b 的距离 f80 dist(auto a, auto b){ returnsqrtl((a - b).abs2()); } // 获得点 a 的单位化向量 autonorm(auto a){ return a / hypotl(a.x, a.y); } // 判断点 p 在向量 ab 的哪一侧 intside(auto p, auto a, auto b){ returnsgn((a - p) ^ (b - p)); } // 判断点 p 是否在线段 ab 上 boolons(auto p, auto a, auto b){ returnside(p, a, b) == 0 && sgn((a - p) * (b - p)) <= 0; } // 判断线段 ab 与线段 cd 是否存在交点 booliss(auto a, auto b, auto c, auto d){ if (ons(a, c, d) || ons(b, c, d) || ons(c, a, b) || ons(d, a, b)) returntrue; if (side(a, b, c) * side(a, b, d) >= 0) returnfalse; if (side(c, d, a) * side(c, d, b) >= 0) returnfalse; returntrue; } // 判断直线 ab 与线段 cd 是否存在交点 boolils(auto a, auto b, auto c, auto d){ returnside(a, b, c) * side(a, b, d) <= 0; } // 判断直线 ab 与直线 cd 是否平行 boolpara(auto a, auto b, auto c, auto d){ returnsgn(a - b ^ c - d) == 0; } // 获得直线 ab 与直线 cd 的交点 Pf ill(auto a, auto b, auto c, auto d){ auto u = b - a, v = d - c; auto t = (a - c ^ v) / (v ^ u); return a + u * t; } // 获取点 p 在直线 uv 上的垂足 Pf foot(auto p, auto u, auto v){ auto dir = norm(u - v); return u + dir * (dir * (p - u)); } // 获取向量 a 逆时针旋转角度 r 后的向量, r ∈ [0, PI] Pf rot(auto a, f80 r){ Pf b = {sinl(r), cosl(r)}; return {a ^ b, a * b}; } // 获取三角形 ABC 的面积 f80 tria(auto a, auto b, auto c){ returnabs(b - a ^ c - a) / 2; }
// 获取闭合多边形 p 的面积 f80 polya(auto &p){ f80 res = 0; for (int i = 0; i < p.size(); ++i) res += p[i] ^ p[(i + 1) % p.size()]; returnabs(res) / 2; }
structwint : private vector<int> { staticconstexprint p = 1000000000, w = 9;
wint() : vector(1) { }
explicit(false) wint(const string &s) { for (int i = (int) s.size(); i >= 1; i -= w) { int begin = max(0, i - w); push_back(stoi(s.substr(begin, i - begin))); } }
explicit(false) wint(u64 n) { if (n == 0) push_back(0); while (n > 0) push_back((int) (n % p)), n /= p; }
friend ostream &operator<<(ostream &out, const wint &self) { out << self[self.size() - 1]; for (int i = (int) self.size() - 2; i >= 0; --i) out << setw(w) << setfill('0') << self[i]; return out; }
wint operator+(const wint &other) const { wint res(""); int t = 0; for (int i = 0; i < max(size(), other.size()); ++i) { if (i < size()) t = t + operator[](i); if (i < other.size()) t += other[i]; res.push_back(t % p); t /= p; } if (t) res.push_back(t); return res; }
wint operator-(const wint &other) const { wint res(""); int t = 0; for (int i = 0; i < size(); ++i) { t = operator[](i) - t; if (i < other.size()) t -= other[i]; res.push_back((t + p) % p); if (t < 0) t = 1; else t = 0; } while (res.size() > 1 && res.back() == 0) res.pop_back(); return res; }