// 尺取法获取第k近的点下标 voidgetKth(int k){ int l = 1, r = k + 1; nxt[1] = k + 1; for (int i = 2; i <= n; i++) { while (r + 1 <= n && a[i] - a[l] > a[r + 1] - a[i]) l++, r++; if (a[i] - a[l] < a[r] - a[i]) nxt[i] = r; else nxt[i] = l; } }
1 2 3 4 5 6 7 8 9 10 11 12
voidadd(int x){ if (++mp[a[x]] == 1) cnt++; } voiddel(int x){ if (--mp[a[x]] == 0) cnt--; } // 尺取法,f[i] 表示区间 [i, f[i]] 中有k个不同的数 for (int l = 1, r = 1; l <= n; l++) { while (cnt < k && r <= n) add(r++); if (cnt == k) f[l] = r - 1; else f[l] = n + 1; del(l); }
1 2 3 4 5 6 7 8 9 10 11 12
voidadd(int x){ sum += a[x]; } voiddel(int x){ sum -= a[x]; } sum = 0, res = n + 1; // 查询满足区间和大于等于 s 的最短区间 for (int l = 1, r = 1; l <= n; l++) { while (sum < s && r <= n) add(r++); if (sum < s) break; res = min(res, r - l); del(l); }
二分和二分答案
$O(\log n)$
1 2 3 4 5 6 7 8 9 10
int l, r, m; // [l,r) while (r - l > 1) if (check(m = (l + r) / 2)) l = m; else r = m; // l : last true // r : first false // lower_bound(x) | check(mid) <x -> r | // upper_bound(x) | check(mid)<=x -> r |
倍增
快速幂式
$O(n\log m)$
1 2 3 4 5 6 7 8 9 10
// n个点跳跃m次位置 vector<ll> f(nxt); for (; m; m >>= 1) { if (m & 1) for (int i = 1; i <= n; i++) ans[i] = f[ans[i]]; //从上次的位置接着跳 vector<ll> ff(f); for (int i = 1; i <= n; i++) f[i] = ff[ff[i]]; //倍增跳 }
朴素式
$O(n\log m)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 初始化 vector<vector<ll>> f(n + 1, vector<ll>(__lg(m) + 2)); for (int i = 1; i <= n; i++) f[i][0] = nxt[i]; for (int t = 1; t <= __lg(m) + 1; t++) for (int i = 1; i <= n; i++) f[i][t] = f[f[i][t - 1]][t - 1]; // 查询 int p = 1; for (int t = __lg(m) + 1; t >= 0; t--) if (f[p][t] is 合法) { p = f[p][t]; 其他操作 }
intcompress(vector<pii> &a, vector<int> &d){ for (auto &[l, r] : a) d.push_back(l), d.push_back(r); sort(d.begin(), d.end()); d.erase(unique(d.begin(), d.end()), d.end()); auto get = [&d](int x) { returnlower_bound(d.begin(), d.end(), x) - d.begin(); }; for (auto &[l, r] : a) l = get(l), r = get(r); return d.size(); } // 离散后修改 vector<int> f(compress(a, d)); for (auto &[l, r] : a) fill(f.begin() + l, f.begin() + r, 1); // 逆映射到离散前的原数值 for (int i = 0; i < f.size() - 1; i++) if (f[i]) ans += d[i + 1] - d[i];
二维的可以对两个维度分别离散化
排列和组合枚举
全排列
$O(n!)$
1 2 3 4 5
vector<int> a(n); iota(a.begin(), a.end(), 1); do { // 判断排列的合法性 } while (next_permutation(a.begin(), a.end()));
组合
$O(2^n)$
1 2 3 4 5 6 7 8 9 10 11 12
int n = 4; vector<int> a(n + 1); for (int i = 0; i < (1 << n); i++) { int cnt = __builtin_popcount(i); a[cnt]++; for (int j = 0; j < n; j++) if (i >> j & 1) { // 判断某位 } } for (int i = 0; i <= n; i++) cout << a[i] << " ";
数据结构
单调栈
维护某值的左边第一大,右边第一小 $O(n)$
1 2 3 4 5 6 7 8 9 10
stack<int> s; for (int i = 0; i < n; i++) { while (!s.empty() && a[s.top()] < a[i]) {// 单调递减栈 // 此时a[i]是第一个 > a[s.top()]的数 s.pop(); } // 此时a[i]左边有 s.size() 个 >= a[i] 的数 // 此时a[i]是第一个 <= a[s.top()]的数 s.push(i); }
单调队列
维护k区间最值 $O(n)$
1 2 3 4 5 6 7 8 9 10
deque<int> q; for (int i = 0; i < n; i++) { while (!q.empty() && i - q.front() >= k) // 调整覆盖范围 q.pop_front(); while (!q.empty() && a[q.back()] < a[i]) // 保持单调递减 q.pop_back(); q.push_back(i); if (i >= k - 1) cout << a[q.front()] << endl; // 区间[i-k+1,i]最大值 }
ST表/稀疏表/Sparse Table
维护区间最值
预处理: $O(n\log n)$
查询: $O(1)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
template <typename T, T (*op)(T, T)> structSparseTable { int n, logn; vector<vector<T>> dat; SparseTable(const vector<T> &v) : n(v.size()), logn(__lg(n) + 1), dat(n + 1, vector<T>(logn + 1)) { for (int i = 1; i <= n; i++) dat[i][0] = v[i - 1]; for (int j = 1; j <= logn; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) dat[i][j] = op(dat[i][j - 1], dat[i + (1 << j - 1)][j - 1]); } T query(int l, int r){ int s = __lg(r - l + 1); returnop(dat[l][s], dat[r - (1 << s) + 1][s]); } }; intmaxn(int a, int b){ return a > b ? a : b; }
并查集
维护集合的合并与查询是否相交
初始化: $O(n)$
查询/合并: $O(\log n)$
1 2 3 4 5 6 7
structDSU { vector<int> p, sz; DSU(int n = 1e6) : p(n), sz(n, 1) { iota(p.begin(), p.end(), 0); } intfind(int x){ return x == p[x] ? x : p[x] = find(p[x]); } voidmerge(int x, int y){ x = find(x), y = find(y), (x != y ? p[y] = x, sz[x] += sz[y] : 0); } intsize(int x){ return sz[find(x)]; } };
template <typename T> structBIT { int size; vector<T> dat; BIT(int n = 0) : size(n), dat(n + 1, 0) {} inlineintlowbit(int x){ return x & -x; } voidadd(int i, T x){ for (; i <= size; i += lowbit(i)) dat[i] += x; } T get(int i){ T res = 0; for (i = min(i, size); i; i -= lowbit(i)) res += dat[i]; return res; } T query(int l, int r){ returnget(r) - get(l - 1); } intkthLe(int k){ int l = 0, r = size + 1, mid; while (r - l > 1) get(mid = (l + r) >> 1) < k ? l = mid : r = mid; return r; } intkthGe(int k){ int l = 0, r = size + 1, mid; int sum = get(size); while (r - l > 1) sum - get(mid = (l + r) >> 1) < k ? r = mid : l = mid; return r; } };
template <typename Info> structSegmentTree { int n; vector<Info> info; SegmentTree() : n(0) {} SegmentTree(int n_) : n(n_), info(4 << __lg(n)) {} SegmentTree(const vector<Info> &a) : SegmentTree(a.size()) { function<void(int, int, int)> build = [&](int p, int l, int r) { if (l == r) returnvoid(info[p] = a[l - 1]); int m = (l + r) / 2; build(p << 1, l, m); build(p << 1 | 1, m + 1, r); info[p] = info[p << 1] + info[p << 1 | 1]; }; build(1, 1, n); } voidmodify(int p, int l, int r, int i, const Info &v){ if (i < l || r < i) return; if (l == r) returnvoid(info[p] = v); int m = (l + r) / 2; modify(p << 1, l, m, i, v); modify(p << 1 | 1, m + 1, r, i, v); info[p] = info[p << 1] + info[p << 1 | 1]; } voidmodify(int x, const Info &v){ modify(1, 1, n, x, v); } Info rangeQuery(int p, int l, int r, int a, int b){ if (b < l || r < a) returnInfo(); if (a <= l && r <= b) return info[p]; int m = (l + r) / 2; returnrangeQuery(p << 1, l, m, a, b) + rangeQuery(p << 1 | 1, m + 1, r, a, b); } Info rangeQuery(int a, int b){ returnrangeQuery(1, 1, n, a, b); } };
template <typename Info, typename Tag> structLazySegmentTree { int n; vector<Info> info; vector<Tag> tag; LazySegmentTree() : n(0) {} LazySegmentTree(int n_) : n(n_), info(4 << __lg(n)), tag(4 << __lg(n)) {} LazySegmentTree(const vector<Info> &a) : LazySegmentTree(a.size()) { function<void(int, int, int)> build = [&](int p, int l, int r) { if (l == r) returnvoid(info[p] = a[l - 1]); int m = (l + r) / 2; build(p << 1, l, m); build(p << 1 | 1, m + 1, r); info[p] = info[p << 1] + info[p << 1 | 1]; }; build(1, 1, n); } voidapply(int p, int l, int r, const Tag &v){ info[p].apply(l, r, v); tag[p].apply(l, r, v); } voidpushdown(int p, int l, int r){ int m = (l + r) / 2; apply(p << 1, l, m, tag[p]); apply(p << 1 | 1, m + 1, r, tag[p]); tag[p] = Tag(); } voidmodify(int p, int l, int r, int x, const Info &v){ if (x < l || r < x) return; if (l == r) returnvoid(info[p] = v); pushdown(p, l, r); int m = (l + r) / 2; modify(p << 1, l, m, x, v); modify(p << 1 | 1, m + 1, r, x, v); info[p] = info[p << 1] + info[p << 1 | 1]; } voidmodify(int x, const Info &v){ modify(1, 1, n, x, v); } voidrangeApply(int p, int l, int r, int a, int b, const Tag &v){ if (b < l || r < a) return; if (a <= l && r <= b) returnapply(p, l, r, v); pushdown(p, l, r); int m = (l + r) / 2; rangeApply(p << 1, l, m, a, b, v); rangeApply(p << 1 | 1, m + 1, r, a, b, v); info[p] = info[p << 1] + info[p << 1 | 1]; } voidrangeApply(int a, int b, const Tag &v){ rangeApply(1, 1, n, a, b, v); } Info rangeQuery(int p, int l, int r, int a, int b){ if (b < l || r < a) returnInfo(); if (a <= l && r <= b) return info[p]; pushdown(p, l, r); int m = (l + r) / 2; returnrangeQuery(p << 1, l, m, a, b) + rangeQuery(p << 1 | 1, m + 1, r, a, b); } Info rangeQuery(int a, int b){ returnrangeQuery(1, 1, n, a, b); } }; structTag { int x; voidapply(int l, int r, const Tag &v){ x += v.x; } }; structInfo { ll sum = 0; voidapply(int l, int r, const Tag &v){ sum += (r - l + 1) * v.x; } friend Info operator+(Info a, Info b) { return Info{a.sum + b.sum}; } }; // void modifty(int a, int b, T k, int p = 1) { // if (a <= tr[p].l && tr[p].r <= b) // return mark(k, p); // pushdown(p); // int mid = (tr[p].l + tr[p].r) >> 1; // if (a <= mid) modifty(a, b, k, p << 1); // 左半区间与修改区间相交 // if (b > mid) modifty(a, b, k, p << 1 | 1); // 右半区间与修改区间相交 // tr[p] = tr[p << 1] + tr[p << 1 | 1]; // } // Node query(int a, int b, int p = 1) { // if (a <= tr[p].l && tr[p].r <= b) // return tr[p]; // pushdown(p); // int mid = (tr[p].l + tr[p].r) >> 1; // if (b <= mid) return query(a, b, p << 1); // 只有左半区间与查询区间相交 // else if (a > mid) return query(a, b, p << 1 | 1); // 只有右半区间与查询区间相交 // else return query(a, b, p << 1) + query(a, b, p << 1 | 1); // (a <= mid < b) 两端区间都与查询区间相交 // }
template <typename T> classBlock { int n, t, m; vector<int> l, r, id; vector<T> &v, sum, tag; voidadd(int p, int a, int b, T k){ sum[p] += k * (b - a + 1); for (int i = a; i <= b; i++) v[i] += k; } T ask(int p, int a, int b){ T res = tag[p] * (b - a + 1); for (int i = a; i <= b; i++) res += v[i]; return res; }
public: Block(vector<T> &v) // 1-index : n(v.size() - 1), t(sqrt(n)), m(n / t + (n % t > 0)), l(m + 1), r(m + 1), id(n + 1), v(v), sum(m + 1), tag(m + 1) { for (int i = 1; i <= m; i++) { l[i] = (i - 1) * t + 1, r[i] = min(i * t, n); for (int j = l[i]; j <= r[i]; j++) { sum[i] += v[j]; id[j] = (j - 1) / t + 1; } } } voidupdate(int a, int b, T k){ int p = id[a], q = id[b]; if (p == q) { add(p, a, b, k); } else { for (int i = p + 1; i <= q - 1; i++) tag[i] += k; add(p, a, r[p], k); add(q, l[q], b, k); } } T query(int a, int b){ T res = 0; int p = id[a], q = id[b]; if (p == q) { res += ask(p, a, b); } else { for (int i = p + 1; i <= q - 1; i++) res += sum[i] + tag[i] * (r[i] - l[i] + 1); res += ask(p, a, r[p]); res += ask(q, l[q], b); } return res; } };
using Node = pair<int, int>; vector<int> G[N]; int G[N][N]; // int n, m;
for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back({v, w}); G[v].push_back({u, w}); G[u][v] = G[v][u] = w; } for (int u = 0; u < n; u++) for (auto &[v, w] : G[u]) {} for (int v = 0; v < n; v++) if (G[u][v]) {}
structChainForwardStar { using Edge = pair<int, int>; vector<int> head, next; vector<Edge> edges; int E; ChainForwardStar(int V = 0) { head.resize(V + 1, -1); } voidaddEdge(int u, int v, int w = 1){ edges.push_back({v, w}); next.push_back(head[u]); head[u] = E++; } }; ChainForwardStar G(n); for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; G.addEdge(u, v, w); } for (int u = 0; u < n; u++) for (int i = G.head[u]; ~i; i = G.next[i]) { auto &[v, w] = G.edges[i]; cout << u << " " << v << " " << w << endl; }
booltsort(){ queue<int> q; vector<int> ans; for (int u = 1; u <= n; u++) for (int v : G[u]) indeg[v]++; for (int u = 1; u <= n; u++) if (indeg[u] == 0) q.push(u); while (!q.empty()) { int u = q.front(); q.pop(); ans.push_back(u); for (int v : G[u]) if (--indeg[v] == 0) q.push(v); } return ans.size() == n; }
#include<bits/stdc++.h> usingnamespace std; constint N = 1e5 + 5; int n; vector<int> G[N]; int dep[N], p[N][20]; voiddfs(int u, int fa){ p[u][0] = fa; for (int t = 1; (1 << t) <= dep[u]; t++) p[u][t] = p[p[u][t - 1]][t - 1]; for (int v : G[u]) if (v != fa) { dfs(v, u); dep[v] = dep[u] + 1; } } intLCA(int x, int y){ if (dep[x] < dep[y]) swap(x, y); for (int i = 19; i >= 0; i--) if (dep[x] - (1 << i) >= dep[y]) x = p[x][i]; if (x == y) return x; for (int i = 19; i >= 0; i--) if (p[x][i] != p[y][i]) x = p[x][i], y = p[x][i]; return p[x][0]; } intmain(){ cin >> n; for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } dfs(1, 0); }
// x 代表当前搜索树的根节点,in_edge 代表其对应的序号(tot) voidtarjan(int x, int in_edge){ // 在搜索之前,先初始化节点 x 的时间戳与追溯值 dfn[x] = low[x] = ++num; // 通过 head 变量获取节点 x 的直接连接的第一个相邻节点的序号 // 通过 Next 变量,迭代获取剩下的与节点 x 直接连接的节点的序号 for (int i = head[x]; i; i = Next[i]) { // 此时,i 代表节点 y 的序号 int y = ver[i]; // 如果当前节点 y 没有被访问过 if (!dfn[y]) { // 递归搜索以 y 为跟的子树 tarjan(y, i); // 计算 x 的追溯值 low[x] = min(low[x], low[y]); // 桥的判定法则 if (low[y] > dfn[x]) bridge[i] = bridge[i ^ 1] = true; // 标记当前节点是否为桥(具体见下文) } elseif (i != (in_edge ^ 1)) // 当前节点被访问过,且 y 不是 x 的“父节点”(具体见下文) low[x] = min(low[x], dfn[y]); } }
intgetBit(int a, int b){ return (a >> b) & 1; } intunsetBit(int a, int b){ return a & ~(1 << b); } intsetBit(int a, int b){ return a | (1 << b); } intflapBit(int a, int b){ return a ^ (1 << b); }
int __builtin_popcount(int n) // 返回n的二进制中有多少个1 int __builtin_parity(int n) // 判断n的二进制中1的个数的奇偶性,偶0奇1 int __builtin_ffs(int n) // 返回n的二进制末尾最后一个1的位置,从一开始 int __builtin_clz(int n) // 返回n的二进制前导0的个数,当n为0时,结果未定义 int __builtin_ctz(int n) // 返回n的二进制后导0的个数 int __builtin_popcountll(longlong n) // 支持long long
n & (n - 1) // 判断n是否为2的次幂,是0否1 n & -n; // lowbit 最低位1
for (int s = 0; s < (1 << n); ++s) // 降序遍历 m 的非空子集 for (int s = m; s; s = (s - 1) & m) // s 是 m 的一个非空子集
快速幂
$O(\log n)$
1 2 3 4 5 6 7
ll pow(ll x, ll n){ ll s = 1; for (; n; n >>= 1, x = x * x) if (n & 1) s = s * x; return s; }
数论
最大公约数
欧几里得算法/辗转相除法
$O(\log n)$
1 2 3
intgcd(int a, int b){ return b ? gcd(b, a % b) : a; } // c++14 : __gcd(a, b) // c++17 : std::gcd(a, b) <numeric>
扩展欧几里得算法
求 $ax+by=\gcd(a,b)$ 的一组特解
$O(\log n)$
1 2 3 4 5 6 7 8 9 10
intextgcd(int a, int b, int &x, int &y){ int d; if (b == 0) x = 1, y = 0, d = a; else { d = extgcd(b, a % b, y, x); // x' y' y -= a / b * x; // y = y - a / b * x = x' - a / b * y' = y'' - a / b * y' } return d; }
// ax + by = c boolliEu(ll a, ll b, ll c, ll &x, ll &y){ int d = extgcd(a, b, x, y); if (c % d != 0) returnfalse; x = x * c / d; y = y * c / d; returntrue; }
intinv(int a, int b){ int x, y; extgcd(a, b, x, y); return x; }
快速幂法
$ax \equiv 1 \pmod b \iff ax\equiv a^{b-1} \pmod b \iff x \equiv a^{b-2} \pmod b$
1
intinv(int a, int b){ returnpow_mod(a, b - 2, b); }
线性同余方程
$ax \equiv b \pmod n$
逆元法
$ax \equiv b \pmod n\iff x \equiv ba^{-1} \pmod n$
扩展欧几里得法
$ax \equiv b \pmod n \iff ax+ny=b$
线性同余方程组:中国剩余定理
1 2 3 4 5 6 7 8 9 10
LL CRT(int k, LL* a, LL* r){ LL n = 1, ans = 0; for (int i = 1; i <= k; i++) n = n * r[i]; for (int i = 1; i <= k; i++) { LL m = n / r[i], b, y; exgcd(m, r[i], b, y); // b * m mod r[i] = 1 ans = (ans + a[i] * m * b % n) % n; } return (ans % n + n) % n; }
素数
分解质因数/唯一分解定理
$O(\sqrt{n})$
1 2 3 4 5 6 7 8 9 10 11 12 13
vector<int> getPrimeFactor(int n){ vector<int> res; for (int i = 2; i * i <= n; i++) if (n % i == 0) { int cnt = 0; while (n % i == 0) n /= i, cnt++; res.push_back(i); } if (n != 1) res.push_back(n); return res; }
素数测试
$O(\sqrt{n})$
1 2 3 4 5 6
boolPrime(int x){ for (int i = 2; i * i <= x; i++) if (x % i == 0) returnfalse; return x > 1; }
boolmillerRabin(int n, int k = 50){ if (n < 3 || n % 2 == 0) return n == 2; int u = n - 1, t = 0; while (u % 2 == 0) u /= 2, ++t; // test_time 为测试次数,建议设为不小于 8 // 的整数以保证正确率,但也不宜过大,否则会影响效率 for (int i = 0; i < k; ++i) { int a = rand() % (n - 2) + 2, v = qpow(a, u, n); if (v == 1) continue; int s; for (s = 0; s < t; ++s) { if (v == n - 1) break; // 得到平凡平方根 n-1,通过此轮测试 v = (longlong)v * v % n; } // 如果找到了非平凡平方根,则会由于无法提前 break; 而运行到 s == t // 如果 Fermat 素性测试无法通过,则一直运行到 s == t 前 v 都不会等于 -1 if (s == t) return0; } return1; }
埃氏筛
$O(n\log\log n)$
1 2 3 4 5 6 7 8 9 10 11 12
vector<int> isPrime, primes; voideratosthenes(int n){ isPrime.assign(n + 1, 1); primes.clear(); isPrime[0] = isPrime[1] = 0; for (int i = 2; i <= n; i++) if (isPrime[i]) { primes.push_back(i); for (int j = 2 * i; j <= n; j += i) isPrime[j] = 0; } }
线性筛
$O(n)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14
vector<int> minp, primes; voidsieve(int n){ minp.assign(n + 1, 0); primes.clear(); for (int i = 2; i <= n; i++) { if (!minp[i]) minp[i] = i, primes.push_back(i); for (int j = 0; i * primes[j] <= n; j++) { minp[i * primes[j]] = primes[j]; if (i % primes[j] == 0) break; } } }
积性函数
如果有 $\gcd(a, b) = 1$ , 那么 $\varphi(a \times b) = \varphi(a) \times \varphi(b)$
inteuler_phi(int n) int ans = n; for (int i = 2; i * i<= n; i++) if (n % i == 0) { ans = ans / i * (i - 1); while (n % i == 0) n /= i; } if (n > 1) ans = ans / n * (n - 1); return ans; }
int f[N], g[N]; ll s[N]; ll H(int n){ ll res = 0; for (ll l = 1, r; l <= n; l = r + 1) { ll d = n / l; r = n / d; // res += (r - l + 1) * d; res += (s[r] - s[l - 1]) * g[d]; } return res; }
int mod = 1e9 + 7; template <typename T> T pow(T a, longlong b){ T s = 1; for (; b; a = a * a, b >>= 1) if (b & 1) s = s * a; return s; } structZ { longlong x; Z() {} Z(longlong x) : x(Norm(x)) {} staticlonglongNorm(longlong x){ return x %= mod, x < 0 ? x + mod : x; } Z inv()const{ returnpow(*this, mod - 2); } friend Z operator+(const Z &a, const Z &b) { returnZ(a.x + b.x); } friend Z operator-(const Z &a, const Z &b) { returnZ(a.x - b.x); } friend Z operator*(const Z &a, const Z &b) { returnZ(a.x * b.x); } friend Z operator/(const Z &a, const Z &b) { return a * b.inv(); } friend ostream &operator<<(ostream &os, const Z &z) { return os << z.x; } friend istream &operator>>(istream &os, Z &z) { return os >> z.x, z.x = Norm(z.x), os; } };
组合数学
排列数和组合数
$O(n)$ 预处理,$O(1)$ 查询
1 2 3 4 5 6 7 8 9 10
structBinom { vector<Z> fac; Binom(int n = 0) : fac(n + 1) { fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i; } Z C(int m, int n){ return (m < n || n < 0) ? 0 : fac[m] / fac[n] / fac[m - n]; } Z P(int m, int n){ return (m < n || n < 0) ? 0 : fac[m] / fac[m - n]; } } binom;
卢卡斯定理
1 2 3 4 5
Z lucas(int n, int m, int p){ if (n < p && m < p) return binom.C(n, m); return binom.C(n % p, m % p) * lucas(n / p, m / p, p); }
线性代数
向量与矩阵
矩阵快速幂
$k^3 O(\log n)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
using ll = longlong; using Matrix = vector<vector<int>>; Matrix operator*(const Matrix &A, const Matrix &B) { Matrix C(A.size(), vector<int>(B[0].size())); for (int i = 0; i < A.size(); i++) for (int k = 0; k < B.size(); k++) for (int j = 0; j < B[0].size(); j++) C[i][j] = (C[i][j] + A[i][k] * B[k][j]); return C; } Matrix pow(Matrix &A, ll n){ Matrix B(A.size(), vector<int>(A[0].size())); for (int i = 0; i < B.size(); i++) B[i][i] = 1; for (; n; n >>= 1, A = A * A) if (n & 1) B = B * A; return B; }
using Poly = vector<int>; using Complex = complex<double>; constdouble eps = 0.49; constdouble PI = acos(-1); int rev[1 << 22]; voidFFT(Complex a[], int n, int inv){ for (int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << __lg(n >> 1)); for (int i = 0; i < n; ++i) if (i < rev[i]) swap(a[i], a[rev[i]]); for (int i = 2; i <= n; i <<= 1) { Complex wn(cos(2 * PI / i), inv * sin(2 * PI / i)); for (int j = 0; j < n; j += i) { Complex w0(1, 0); for (int k = j; k < j + i / 2; ++k, w0 *= wn) { Complex x = a[k], y = w0 * a[k + i / 2]; a[k] = x + y; a[k + i / 2] = x - y; } } } if (inv == -1) for (int i = 0; i < n; i++) a[i] /= n; } Poly operator*(Poly a, Poly b) { int n = a.size() + b.size() - 1; vector<Complex> c(2 << __lg(n - 1)); for (int i = 0; i < a.size(); i++) c[i].real(a[i]); for (int i = 0; i < b.size(); i++) c[i].imag(b[i]); FFT(c.data(), c.size(), 1); for (int i = 0; i < c.size(); i++) c[i] = c[i] * c[i]; FFT(c.data(), c.size(), -1); Poly s(n); for (int i = 0; i < n; i++) s[i] = (int)(c[i].imag() / 2 + eps); return s; }
structTire { int nxt[100000][26], cnt; bool exist[100000]; // 该结点结尾的字符串是否存在
voidinsert(string s){ // 插入字符串 int p = 0; for (char c : s) { c -= 'a'; if (!nxt[p][c]) nxt[p][c] = ++cnt; // 如果没有,就添加结点 p = nxt[p][c]; } exist[p] = 1; }
boolfind(string s){ // 查找字符串 int p = 0; for (char c : s) { c -= 'a'; if (!nxt[p][c]) returnfalse; p = nxt[p][c]; } return exist[p]; } };
KMP
next数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// pi[i]: 子串 s[0, i-1] 最长的相等的真前缀与真后缀的长度 // pi[i]: s[0, pi[i]-1] == s[i-pi[i], i-1] vector<int> prefix(string s){ int n = s.size(); vector<int> pi(n + 1); for (int i = 1; i < n; i++) { int j = pi[i]; while (j && s[i] != s[j]) j = pi[j]; if (s[i] == s[j]) j++; pi[i + 1] = j; } return pi; }