ACM CS C++ Code
个人码风规定
不用奇怪的define! 少用神秘的位运算! 拒绝各种邪教码风!
基础模板
1 |
|
常用变量名
大小写敏感
1 | T // 样例数 |
缩进/空格/函数名
所有函数/if/for等语句大括号不换行
能加空格尽量加,除非忍不住
驼峰命名法 尽量不用下划线
1 | // 驼峰命名法 |
*关于索引:其实我一般数组、图论都是从 $1$ 到 $n$,但是有时候会出现字符串等特殊情况就用 $0 $到 $n-1$
常用模板
基础算法
快读/快写
实际几乎没用过,打比赛基本不卡常,大数据IO用scanf/printf够了
1 | int read() { |
像我这种喜欢用cin/cout的,建议补上这个关闭流同步:
1 | cin.tie(nullptr); |
二分答案
最基础最重要的算法之一
实际使用主要需要设计二分条件,即half函数
这里给一个例子:
在一个排好序的数组 $s$ 里面寻找整数 $a$ 的位置,时间复杂度 $O(log(n))$
1 | int s[MAXN]; |
快速排序
不会真的有人不用STL的sort,还自己写快排吧(
1 | int s[MAXN]; |
数学
快速幂
可以在 $O(\log n)$ 的时间复杂度里求出 $a^n \bmod m$
1 | LL fastPow(LL a, LL n, LL m = MOD) { |
GCD 欧几里得算法
求a和b的最大公约数
1 | int gcd(int a, int b) { |
(其实STL有函数__gcd(a, b))
EXGCD 扩展欧几里得算法
在求得 $a$ 和 $b$ 的最大公约数的同时,能找到整数 $x$ 和 $y$,满足贝祖等式
$$
ax + by = \gcd(a, b)
$$
1 | void exgcd(int a, int b, int &x, int &y) { |
对于不定方程
$$
ax+by = p
$$
- 如果 $p$ 不是 $\gcd(a,b)$ 的倍数,此不定方程没有整数解
- 如果 $p$ 是其倍数,则有无穷多解:
$$
x = x_0 + k\left(\frac{b}{\gcd(a,b)}\right) \\
y = y_0 - k\left(\frac{a}{\gcd(a,b)}\right)
$$
逆元
单个逆元的式子(根据费马小定理推导),快速幂直接算
$$
a^{-1} \equiv a^{m-2} \pmod{m}
$$
1 | LL modinv(LL a) { |
用 $O(n)$ 的时间求 $1$ 到 $n$ 对模 $m$ 的逆元
$$
ax \equiv 1 \pmod{m}
$$
1 | int n, m, inv[MAXN]; |
有理数取余
其实就是逆元
$$
\frac{a}{b} \equiv a \cdot b^{m-2} \pmod{m}
$$
分数
封装好的分数结构体,重载了加减乘除,所有计算在模MOD意义下进行
1 | struct Fraction { |
CRT 中国剩余定理
用于解以下一元线性同余方程组
$$
\begin{cases}
x \equiv a_1 \pmod{m_1} \\
x \equiv a_2 \pmod{m_2} \\
\ \ \ \vdots \\
x \equiv a_n \pmod{m_n}
\end{cases}
$$
所有模数 $m_i$ 互质
注意配合前面的exgcd使用
考虑到实际情况一般答案会很大,建议开long long
1 | LL CRT(LL n, LL *m, LL *a) { |
EXCRT 扩展中国剩余定理
还是解上面的同余方程组,但是模数可以不互质
1 | LL EXCRT(LL n, LL *m, LL *a) { |
组合数
常用式子
定义:
$$
\binom{n}{k} = C(n, k) = \frac{n!}{k!(n-k)!}
$$
Pascal恒等式:
$$
\binom{n}{k} = \binom{n-1}{k} + \binom{n-1}{k-1}
$$
二项式定理:
$$
(a+b)^n = \sum_{k=0}^n \binom{n}{k} a^{n-k} b^k
$$
当 $a = b = 1$ 时,有:
$$
\sum_{k=0}^n \binom{n}{k} = 2^n
$$
模数是质数时
可以用阶乘+逆元快速计算
1 | LL fac[MAXN+1], invfac[MAXN+1]; |
Lucas 卢卡斯定理
模数不是质数时,可以用卢卡斯定理
$$
\binom{n}{k} \equiv
\binom{\lfloor n/p \rfloor}{\lfloor k/p \rfloor}
\binom{n \bmod p}{k \bmod p}
\pmod{p}
$$
适用于 $n,k$ 很大($\approx 10^{18}$)但 $p$ 较小($\approx 10^6$)的情况
1 | LL Cmodp(LL n, LL k, LL p) { |
容斥原理
对于多个集合 $A_1, A_2, \cdots, A_n$ 有:
$$
\begin{align*}
\left| \bigcup_{i=1}^n A_i \right|
&= \sum_{i=1}^n \left| A_i \right| - \sum_{1 \leq i < j \leq n} \left| A_i \cap A_j \right| + \\
& \sum_{1 \leq i < j < k \leq n} \left| A_i \cap A_j \cap A_k \right| + \cdots + (-1)^{n-1} \left| A_1 \cap A_2 \cap \cdots \cap A_n \right| \\
&= \sum_{k=1}^n (-1)^{k-1} \left( \sum_{1 \leq i_1 < i_2 < \cdots < i_k \leq n } \left| A_{i_1} \cap A_{i_2} \cap \cdots \cap A_{i_k} \right| \right) \\
&= \sum_{k=1}^n (-1)^{k-1} \left( \sum_{1 \leq i_1 < i_2 < \cdots < i_k \leq n } \left| \bigcap_{j=1}^k A_{i_j} \right| \right)
\end{align*}
$$
线性代数
矩阵快速幂
给定一个 $n \times n$ 的矩阵 $A$,还有次数 $k$ ,能快速算出 $A^k \bmod M$ ,时间复杂度 $O(n^3 \log k)$ ,适合用于加速 $n$ 比较小,但次数 $k$ 比较大的矩阵幂运算,经常用于优化递推DP式子
1 | const LL MOD = 1e9+7; |
矩阵优化DP
一维数组,多层状态
假设递推是
$$
f_n = a_1 f_{n-1} + a_2 f_{n-2} + \cdots + a_k f_{n-k}
$$
尝试构造矩阵乘法
$$
\begin{bmatrix}
f_n \\
f_{n-1} \\
f_{n-2} \\
\vdots \\
f_{n-k+1}
\end{bmatrix} =
\begin{bmatrix}
a_1 & a_2 & a_3 & \cdots & a_{k-1} & a_k \\
1 & 0 & 0 & \cdots & 0 & 0 \\
0 & 1 & 0 & \cdots & 0 & 0 \\
0 & 0 & 1 & \cdots & 0 & 0 \\
\vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\
0 & 0 & 0 & \cdots & 1 & 0
\end{bmatrix} ^ {n-k}
\begin{bmatrix}
f_{k} \\
f_{k-1} \\
f_{k-2} \\
\vdots \\
f_{1}
\end{bmatrix}
$$
算出来第一行那个元素就是 $f_n$
二维数组,单层状态
形如
$$
\begin{cases}
f_{i,1} = a_{1,1} f_{i-1,1} + a_{1,2} f_{i-1,2} + \cdots + a_{1,k} f_{i-1,k} \\
f_{i,2} = a_{2,1} f_{i-1,1} + a_{2,2} f_{i-1,2} + \cdots + a_{2,k} f_{i-1,k} \\
\ \ \vdots \\
f_{i,k} = a_{k,1} f_{i-1,1} + a_{k,2} f_{i-1,2} + \cdots + a_{k,k} f_{i-1,k}
\end{cases}
$$
可以尝试构造矩阵乘法
$$
\begin{bmatrix}
f_{n,1} & f_{n,2} & \cdots & f_{n,k}
\end{bmatrix}=
\begin{bmatrix}
f_{1,1} & f_{1,2} & \cdots & f_{1,k}
\end{bmatrix}
\cdot
\begin{bmatrix}
a_{1,1} & a_{2,1} & \cdots & a_{k,1} \\
a_{1,2} & a_{2,2} & \cdots & a_{k,2} \\
\vdots & \vdots & \ddots & \vdots \\
a_{1,k} & a_{2,k} & \cdots & a_{k,k}
\end{bmatrix} ^ {n-1}
$$
高斯消元
解 $n$ 元一次线性方程组,时间复杂度 $O(n^3)$
1 | int n; |
线性基
线性基可以维护一个数组里任意几个数异或的最大值 / 最小值 / 查询是否存在 / 第 $k$ 大的值 / 查询排名。
1 | const int W = 60; |
斐波那契数(递归数)与卡特兰数
递归数
考虑非负整数 $n$ 上的递归关系:
$$
F_n =
\begin{cases}
f_0 & \text{if } n = 0 \\
f_1 & \text{if } n = 1 \\
a \cdot F_{n-1} + b \cdot F_{n-2} & \text{otherwise}
\end{cases}
$$
可以得到通项公式:
$$
F_n = \frac{k^n(f_1 - m f_0) - m^n(f_1 - k f_0)}{k-m} \\
\\
m, k = \frac{a \pm \sqrt{a^2+4b}}{2}
$$
卡特兰数
常用于二叉树、括号匹配、多边形三角划分等问题
$$
\begin{align*}
f_n &= \sum_{i=0}^{n-1}f_i \cdot f_{n-i+1} \\
&= \frac{C_{2n}^n}{n+1} \\
&= C_{2n}^n - C_{2n}^{n-1}
\end{align*}
$$
素数相关
质因数分解
能在 $O(\sqrt{n})$ 的时间复杂度下求出 $n$ 的质因数分解
1 | void Factorize(LL n, vector<int> &factors, map<int,int> &primeCnt) { |
欧拉线性筛素数
能在 $O(n)$ 的时间复杂度下快速筛出 $1$ 到 $n$ 的所有素数
1 | bool isPrime[MAXN]; |
威尔逊定理
$p$ 为素数 $\Leftrightarrow (p-1)! \equiv -1 \pmod p$
费马小定理
若 $p$ 为素数,则:
$$
a^p \equiv a \pmod p
$$
特殊形式:
若 $p$ 为素数,$a$ 为正整数,且 $a$ 和 $p$ 互质,则:
$$
a^{p-1} \equiv 1 \pmod p
$$
欧拉函数和定理
欧拉函数:对于正整数 $n$,欧拉函数 $\phi(n)$ 是小于等于 $n$ 中与 $n$ 互质的数的数目,计算公式:
$$
\phi(n) = n \prod_{i=1}^k \left(1-\frac{1}{p_i}\right)
$$
其中,$p_i$ 是 $n$ 的质因子,而且只计算一次
前置定理:
- 若 $p$ 为素数,则:$\phi(p) = p-1$
- 若 $p$ 为素数,其幂次 $p^a$,则:$\phi(p^a) = (p-1) \cdot p^{a-1}$
- 若 $a$ 与 $b$ 互质,则:$\phi(ab) = \phi(a) \cdot \phi(b)$
欧拉定理:
若 $a$ 与 $m$ 互质,则:
$$
a^{\phi(m)} \equiv 1 \pmod m
$$
可以在 $O(n)$ 的时间复杂度下算出 $\phi(1)$ 到 $\phi(n)$,也可以用 $O(\sqrt{n})$ 的复杂度单次算出 $\phi(n)$
1 | int phi[MAXN], prime[MAXN]; |
BSGS 大步小步算法
能在 $O(\sqrt{p})$ 的时间内求出形如
$$
a^x \equiv b \pmod p
$$
的高次同余方程的解 $x$,或给出无解
1 | // 求解 a^x = b (mod p) |
莫比乌斯反演
给定函数 $F(n)$ 和 $f(n)$ 定义在非负整数集合上,并满足条件:
$$
F(n) = \sum_{d|n} f(d)
$$
则有结论:
$$
f(n) = \sum_{d|n}\mu(d)F\left(\frac{n}{d}\right)
$$
其中,莫比乌斯函数
$$
\mu(n) =
\begin{cases}
1 & n = 1 \\
0 & n\text{ 含有平方因子} \\
(-1)^k & k\text{ 是 }n\text{ 不同的质因子个数}
\end{cases}
$$
也就是说,$n>1$ 的时候,如果能分解成 $k$ 个不同的质因数直接相乘,那么函数值为 $(-1)^k$,否则为 $0$。比如 $\mu(10) = 1$,$\mu(12) = 0$
其有两个性质:
- 对于任意正整数 $n$ 有:$\sum_{d|n} \mu(d) = \begin{cases}1 & n = 1 \\ 0 & n > 1\end{cases}$
- 对于任意正整数 $n$ 有:$\sum_{d|n} \frac{\mu(d)}{d} = \frac{\phi(n)}{n}$
可以在 $O(n)$ 的时间复杂度下算出 $\mu(1)$ 到 $\mu(n)$
1 | bool vis[MAXN]; |
多项式
拉格朗日插值
经过 $n$ 个不同的点可以唯一地确定一个 $n-1$ 次多项式 $y=f(x)$,有拉格朗日插值公式可以在 $O(n^2)$ 的时间内求出 $f(x) \bmod M$
$$
f(k) = \sum_{i=1}^{n} f(x_i) \prod_{j \not= i} \frac{k-x_j}{x_i-x_j}
$$
1 | LL n, xx[MAXN], f[MAXN]; |
没用的板子之用拉格朗日插值法 $O(n^3)$ 解多项式系数
1 | vector <LL> LagrangeInCoef() { |
多项式乘法
给定一个 $n$ 次多项式 $F(x)$ ,和一个 $m$ 次多项式 $G(x)$ ,求出 $F(x) \times G(x)$
FFT
1 | int l, r[MAXN]; |
NTT
1 | int l, r[MAXN]; |
图论
我是邻接表享受者😎
没有特殊说明默认全都是邻接表建图
最短路
SPFA
有向/无向图单源最短路,能求出st到任意一个点的最短路(无负环),时间复杂度最坏 $O(nm)$ ,但实际上会比较快
如果有负环需要加一个cnt数组用于记录入队次数,SPFA里面里判断负环的方法是:记录每个节点入队的次数,如果某个点入队次数超过 $n$(点数),说明存在负环
1 | struct node { |
Dijkstra
有向/无向图单源最短路,能求出st到任意一个点的最短路(无负环),加上小根堆优化后时间复杂度 $O(m \log n)$
这里还加了个输出最短路径
1 | struct node { |
Floyd
有向/无向图多源最短路,时间复杂度 $O(n^3)$ ,代码思路简单所以复杂度也比较高,可以判负环
1 | int n, m; |
然后 main 函数里要这样子初始化以及读边
1 | int main() { |
A*
启发式搜索算法,可以理解为 Dijkstra 的升级版。
1 | struct node { |
这里给个输入样例:
1 | 5 5 |
输出的路径:
1 | ##... |
最小生成树
Kruskal
给定一个无向图,求出最小生成树,模板这里是返回了最小生成树的边权之和,然后把相应的边存起来
时间复杂度 $O(m \log m)$
1 | struct edge { |
Tarjan
割点
无向图中用Tarjan找割点
1 | int n, m, cnt; |
桥
无向图中用Tarjan找桥
1 | struct edge { |
强连通分量
有向图中用Tarjan找强连通分量并染色
1 | int n, m, cnt, colcnt; |
边双连通分量,即无向图Tarjan缩点。记录一个 $last$ 即可
1 | struct edge { |
拓扑排序
有向无环图中(DAG, Directed Acyclic Graph)的每一条有向边( $u \rightarrow v$ ),在排序中顶点 $u$ 必须在顶点 $v$ 的前面
Kahn算法
基于入度的高效算法,还能检测不合法的环,时间复杂度 $O(n+m)$
1 | int n, m; |
树
树的直径
跑两遍dfs找最远的两个点的距离,就是树的直径,时间复杂度 $O(n)$
1 | struct node { |
树的高度
换根DP,时间复杂度 $O(n)$ 能求分别以 $1-n$ 所有节点为根的树的高度
1 | struct node |
LCA
倍增求LCA
1 | int n, m; |
网络流
邻接表享受者😎
Dinic
时间复杂度 $O(n^2m)$
唐逼 GPT 一开始给我生成的板子是错的,藏了半年没发现,被某次大数据卡了就老实了
1 | struct node { |
费用流
最小费用 & 最小费用最大流
基于SPFA,时间复杂度 $O(F*(n+m))$ , $F$ 是最大流总和
1 | struct node { |
二分图匹配
建图:超级原点 $S$ ->左边所有点->右边所有点->超级汇点 $T$ ,每条边流量为 $1$
时间复杂度 $O(\sqrt{n}m)$
1 | int n1, n2, S, T; |
数据结构
并查集
可以用于判断是否在一个连通块内,时间复杂度接近常数
1 | int n, m; |
树状数组
时间 $O(\log{n})$ 单点修改,区间求和,注意这里的 Tsum 查询之后是前缀和
1 | int n; |
ST表
只能维护静态可重复贡献的数据的区间
可重复贡献指的是对于运算 $op$,满足 $x \ op \ x=x$,比如最大值、最小值、最大公因数、最小公倍数、按位与、按位或等等
1 | int n, m, f[MAXN][32], lg2[MAXN]; |
线段树
1 | LL a[MAXN], tree[MAXN<<2], lazy[MAXN<<2]; |
平衡树(AVL)
动态维护一个可重集合 $M$,有以下操作:
- 向 $M$ 中插入一个数 $x$
- 从 $M$ 中删除一个数 $x$(若有多个相同的数,应只删除一个)
- 查询 $M$ 中有多少个数比 $x$ 小,并且将得到的答案加一
- 查询如果将 $M$ 从小到大排列后,排名位于第 $x$ 位的数
- 查询 $M$ 中 $x$ 的前驱(前驱定义为小于 $x$,且最大的数)
- 查询 $M$ 中 $x$ 的后继(后继定义为大于 $x$,且最小的数)
所有操作复杂度均为 $O(\log n)$
1 | struct node { |
字符串
哈希函数
多项式哈希,可以将字符串映射到一个数字上,但要小心哈希冲突
$$
f(s)=\sum_{i=1}^{l} {s[i] \times b^{l-i}} (\text{mod}\ M)
$$
1 |
|
KMP
经典字符串查找算法,时间复杂度 $O(l_1 + l_2)$,即两字符串长度之和
1 | //前缀函数 |
Trie字典树
简单方便的字典树,插入和查询的复杂度都是 $O(L)$,其中 $L$ 表示单词的长度
1 | int son[MAXN][26], cnt[MAXN], idx; |
计算几何
因为我跟孙✌组队打比赛的时候,他已经刷完了 AIZU Online Judge 的计算几何板子(我刷完了图论的),所以我自己没有真正写过完整的计算几何板子,基本上要用的时候就直接荷孙✌的,码风神秘请谅解。
1 | using LD = long double; |
其他
运行批处理
run.bash
1 |
|
对拍批处理
cmp.bash
1 |
|