const int N = 2e5 + 10;
int n, m;
vector<int> e[N];
int dep[N];
int fa[N][20];
void dfs(int u, int father)
{dep[u] = dep[father] + 1; // 设置当前节点深度fa[u][0] = father; // 直接父节点// 预处理二进制提升表for (int i = 1; i <= 19; i++)fa[u][i] = fa[fa[u][i - 1]][i - 1];// 递归处理子节点for (int v : e[u])if (v != father)dfs(v, u);
}
int lca(int u, int v)
{// 确保u是较深的节点if (dep[u] < dep[v])swap(u, v);// 将u提升到与v同一深度for (int i = 19; i >= 0; i--)if (dep[fa[u][i]] >= dep[v])u = fa[u][i];if (u == v)return v; // 如果已经是同一节点// 同时提升u和v直到它们的父节点相同for (int i = 19; i >= 0; i--)if (fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];return fa[u][0]; // 返回LCA
}
11. 倍增
const int N = 2e5 + 10, lg = 23;
int fa[N][lg], mx[N][lg], mn[N][lg];for (int j = 1; j < lg; j++) // 初始化[i][0]for (int i = 1; i <= n; i++){fa[i][j] = fa[fa[i][j - 1]][j - 1];mx[i][j] = max(mx[i][j - 1], mx[fa[i][j - 1]][j - 1]);mn[i][j] = min(mn[i][j - 1], mn[fa[i][j - 1]][j - 1]);}// 预处理次小值
mx2[x][i]=max(max(mx2[f[x][i-1]][i-1],mx2[x][i-1]),mx[x][i-1]==mx[f[x][i-1]][i-1]?0:min(mx[x][i-1],mx[f[x][i-1]][i-1]));
12. 后缀数组
const int N = 1000010; // 找到字典序最大的前缀/后缀
// 𝑠𝑎[𝑖]表示将所有后缀排序后第 𝑖 小的后缀的编号
// 𝑟𝑘[𝑖]表示后缀 𝑖 的排名
// "21">"2" 数字比较字典序可能颠倒 特殊处理
int n, m = 1e5 + 10; // n需要全局 m:数字时设置为值域
int s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];void get_sa()
{for (int i = 1; i <= n; i++)c[x[i] = s[i]]++;for (int i = 1; i <= m; i++)c[i] += c[i - 1];for (int i = n; i; i--)sa[c[x[i]]--] = i;for (int k = 1; k <= n; k <<= 1){int num = 0;for (int i = n - k + 1; i <= n; i++)y[++num] = i;for (int i = 1; i <= n; i++)if (sa[i] > k)y[++num] = sa[i] - k;for (int i = 1; i <= m; i++)c[i] = 0;for (int i = 1; i <= n; i++)c[x[i]]++;for (int i = 1; i <= m; i++)c[i] += c[i - 1];for (int i = n; i; i--)sa[c[x[y[i]]]--] = y[i], y[i] = 0;swap(x, y);x[sa[1]] = 1, num = 1;for (int i = 2; i <= n; i++)x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;if (num == n)break;m = num;}for (int i = 1; i <= n; i++)rk[sa[i]] = i;
}
13. 矩阵快速幂
struct Mat
{int n, m;vector<vector<int>> mat;Mat(int _n, int _m) : n(_n), m(_m), mat(_n, vector<int>(_m, 0)) {} // 下标从0开始const vector<int> &operator[](int i) const { return mat[i]; } // 只读访问vector<int> &operator[](int i) { return mat[i]; } // 可写访问Mat operator*(const Mat &b) const{ // 矩阵乘法assert(m == b.n);Mat res(n, b.m);for (int i = 0; i < n; i++)for (int j = 0; j < b.m; j++)for (int k = 0; k < m; k++)res[i][j] = (res[i][j] + mat[i][k] * b[k][j]) % mod;for (auto vec : res.mat){for (auto v : vec)assert(v >= 0);}return res;}Mat pow(int k) const{ // 矩阵快速幂 须接收返回矩阵assert(n == m);Mat res(n, n);for (int i = 0; i < n; i++)res[i][i] = 1;Mat a = *this;while (k){if (k & 1)res = res * a;a = a * a;k >>= 1;}return res;}
};
14. 计算几何(ll快ld6倍
using ld = long double;
const ld eps = 1e-8; // 9 12 15
const ld PI = acos(-1);
#define Pt Point<T>
#define Pd Point<ld>
#define Pi Point<int>
#define Lt Line<T>
#define Ld Line<ld>
#define Li Line<int>
#define tmp template <class T>int sign(ld x) // 判符号
{if (abs(x) < eps)return 0;elsereturn x < 0 ? -1 : 1;
}
bool equal(ld x, ld y) { return !sign(x - y); } // 近似
bool big(ld has, ld tar) { return sign(has - tar) > 0; }//////////////////////////////////////////////////////////////////////////// 点类// 点与向量
template <class T>
struct Point
{T x, y;Point(T x = 0, T y = 0) : x(x), y(y) {} // 带参构造// 运算符重载Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }Point operator*(T k) const { return Point(x * k, y * k); }Point operator/(T k) const { return Point(1.0 * x / k, 1.0 * y / k); }friend bool operator<(Point a, Point b){if (!equal(a.x, b.x))return a.x < b.x;return a.y < b.y;}friend bool operator>(Point a, Point b) { return b < a; }friend bool operator==(Point a, Point b) { return equal(a.x, b.x) && equal(a.y, b.y); } // 近似friend bool operator!=(Point a, Point b) { return !(a == b); }friend auto &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; }friend auto &operator<<(ostream &os, Point p) { return os << "(" << p.x << ", " << p.y << ")"; }
};tmp ld len(Pt a) { return sqrt(a.x * a.x + a.y * a.y); }; // 模长
tmp ld dis(Pt a, Pt b) { return len(a - b); } // 两点距离
tmp ld dis1(Pt a, Pt b) { return abs(a.x - b.x) + abs(a.y - b.y); } // 曼哈顿距离tmp T cross(Pt a, Pt b) { return a.x * b.y - a.y * b.x; } // 叉积 ab*sin 两点与原点三角形面积 判两点位置关系两侧
tmp T cross(Pt p0, Pt p1, Pt p2) { return cross(p1 - p0, p2 - p0); }
tmp T dot(Pt a, Pt b) { return a.x * b.x + a.y * b.y; } // 点积 ab*cos 结合判两点象限位置关系
tmp T dot(Pt p0, Pt p1, Pt p2) { return dot(p1 - p0, p2 - p0); }
tmp ld angle(Pt a, Pt b) { return atan2(cross(a, b), dot(a, b)); } // 向量夹角[-pi, pi] 非负fabsl
tmp ld angle(Pt p) { return atan2l(p.y, p.x); } // 极角tmp ld area3(Pt a, Pt b, Pt c) { return abs(1.0 * cross(a, b, c)) / 2; } // 三角形面积tmp bool angle0(Pt a, Pt b) { return sqrt(cross(a, b)) < eps && dot(a, b) > 0; } // 夹角为0
tmp bool angle180(Pt a, Pt b) { return cross(a, b) < eps && !angle0(a, b); } // 夹角为180tmp Pd standardize(Pt h) // 转换为单位向量
{ld l = len(h);if (sign(l) == 0)return {0, 0}; // 防止除零return h / l;
}
tmp Pd rotate(Pt p, ld rad) { return {p.x * cos(rad) - p.y * sin(rad), p.x * sin(rad) + p.y * cos(rad)}; } // 向量旋转任意角度tmp Pt rotate90(Pt a) { return Pt{-a.y, a.x}; } // 旋转90° 垂直向量
tmp ld toDeg(ld x) { return x * 180 / PI; } // 弧度转角度
tmp ld toArc(ld x) { return PI / 180 * x; } // 角度转弧度
tmp ld angle(T a, T b, T c) { return acos((a * a + b * b - c * c) / (2.0 * a * b)); } // 余弦定理 三边求角C弧度tmp void psort(vector<Pt> &ps, Pt o = Pt(0, 0)) // 选定极点 极角排序 ✅
{// 三四一二 [ (0, -1), (1, 0), (0, 1), (-1, 0), ]sort(ps.begin(), ps.end(), [&](Pt &A, Pt &B){ A=A-o,B=B-o;auto f=[](Pt A){return A.y>0||(A.y==0&&A.x<0);};if(f(A)!=f(B))return f(A)<f(B);else if(cross(A,B)!=0)return cross(A,B)>0;return dis(A,{0,0})<dis(B,{0,0}); });
}//////////////////////////////////////////////////////////////////////// 直线与线段// 直线
template <class T>
struct Line
{Point<T> a, b;Line(Pt a_ = {}, Pt b_ = {}) : a(a_), b(b_) {}friend auto &operator<<(ostream &os, Line l) { return os << "<" << l.a << ", " << l.b << ">"; }
};
tmp bool pointOnLineLeft(Pt p, Lt l) { return cross(l.b - l.a, p - l.a) > 0; } // 点在直线左侧 ✅
tmp bool onLine(Pt a, Pt b, Pt c) { return sign(cross(a - b, c - b)) == 0; } // 点是否在直线上(三点是否共线)✅
tmp bool onLine(Pt p, Lt l) { return onLine(p, l.a, l.b); }
// 线段:点是否在线段上 ✅
tmp bool pointOnSegment(Pt p, Lt l) { return abs(cross(l.a - p, l.b - p)) < eps && dot(l.a - p, l.b - p) <= 0; }
tmp bool Parallel(Pt p1, Pt p2) { return equal(0, cross(p1, p2)); } // 平行 ✅
tmp bool Orthogonal(Pt p1, Pt p2) { return equal(0, dot(p1, p2)); } // 垂直 ✅
tmp Ld midSegment(Lt l) { return {(l.a + l.b) / 2.0, (l.a + l.b) / 2.0 + rotate90(l.a - l.b)}; } // 线段的中垂线tmp ld disPointToLine(Pt p, Lt l, bool isSegment = false) // 点到直线/线段的最近距离 ✅
{ld len_ab = dis(l.a, l.b);if (sign(len_ab) == 0)return dis(p, l.a);if (!isSegment){ld ans = cross(p, l.a, l.b);return abs(ans) / dis(l.a, l.b); // 面积除以底边长}else{if (dot(p - l.a, l.b - l.a) <= 0)return dis(p, l.a);if (dot(p - l.b, l.a - l.b) <= 0)return dis(p, l.b);ld ans = cross(p, l.a, l.b);return abs(ans) / dis(l.a, l.b);}
}
15. 线段树-结合律-函数复合
struct Node
{mint k, b;
};
#define ls u << 1
#define rs u << 1 | 1
int k[N], b[N];
Node tr[N << 2];
// klx+bl krx+br -> kr(klx+bl)+br -> kl*kr x + kr*bl+br
Node operator+(Node l, Node r)
{Node u;u.k = l.k * r.k;u.b = r.k * l.b + r.b;return u;
}
void bud(int u, int l, int r)
{if (l == r){tr[u] = {k[l], b[l]};return;}int mid = l + r >> 1;bud(ls, l, mid), bud(rs, mid + 1, r);tr[u] = tr[ls] + tr[rs];
}
Node ask(int u, int l, int r, int ql, int qr)
{if (ql <= l && r <= qr)return tr[u];int mid = l + r >> 1;if (qr <= mid)return ask(ls, l, mid, ql, qr);else if (ql > mid)return ask(rs, mid + 1, r, ql, qr);return ask(ls, l, mid, ql, qr) + ask(rs, mid + 1, r, ql, qr);
}
void upd(int u, int l, int r, int tar, int nk, int nb)
{if (l == tar && tar == r){tr[u].k = nk, tr[u].b = nb;return;}int mid = l + r >> 1;if (tar <= mid)upd(ls, l, mid, tar, nk, nb);if (tar > mid)upd(rs, mid + 1, r, tar, nk, nb);tr[u] = tr[ls] + tr[rs];
}
16. 整除分块
int l = 1, r = 0;while (l <= min(n, k)){r = k / (k / l);r = min(r, n);int ans = k / l * (l + r) * (r - l + 1) / 2;res -= ans;l = r + 1;}//二int l = 1, r = 0;for (; l <= n; l = r + 1){int cnt = n / l;r = n / cnt;if (cnt >= k)ans = max(ans, r * r * (1 + cnt) * cnt >> 1);}