Tricks
General Tricks
-
「正难则反」比如说,如果要统计具有某个特征的对象的数量,要 check 一堆东西。此时不妨尝试统计不满足某个特征的对象的数量,用总数量减掉它。e.g. XJOI 7012, 统计含有回文串的数字个数。
-
「强制定向」当一道题有 \(2\) 个方向时,可以把 XX 操作当成一个向量,钦定其唯一方向,但是能有正负。这样就简化了问题。e.g. P5817
-
「推广法」如果不会做数据大时的情况,尝试数据小时的做法,然后推广至普遍做法。e.g. CF1706E
-
「时光倒流」把时间 \(\le k\) 变成 \(\ge n - k + 1\)。e.g. 「NOI2010」航空管制
策略
-
「部分分」部分分有时候有提示性,必须看。
-
「暴搜剪枝」如果你没办法只能写指数级暴力了,必须写剪枝,不然只能拿垫底分,并且必须卡时。e.g. 0723 模拟赛,B 只拿了 10pts
-
「IDA* 想题」你不要在一道题上想太久,如果想 30min 想不出 / 已经搞了个假做法,赶紧上厕所换思路 / 打暴力。e.g. 0723 模拟赛,B 卡了 2h
-
「模拟操作」遇到奇怪的操作要模拟一遍,不然发现不了题目的性质。e.g. xyd 07/18 模拟赛的 B
-
「卡常」有时卡常实在是不可避免的,不要焦躁,不要为一时间咯不懂而忧虑,慢慢卡。看起来循环展开很蠢,实际上优化非常大。有时循环的顺序不太优美,调换一下可以促进内存连续访问。
-
「赌数据」发现由于一些原因复杂度正确的做法常数巨大 / 极其难写 / 写不出来时,可以考虑写一些随机情况下跑得飞快的算法(比如字符串暴力匹配一类)。
-
「大模拟」大模拟,需要头脑冷静,不急不躁,想明白程序结构,尽量做好模块化。
-
「超时强退」如果我们不会复杂度对的方法,可以考虑计算程序运行时间,如果快超时了直接瞎搞几下并 return。如果数据有点弱,可以骗很多分。
数据结构
-
线段树
-
线段树维护奇怪东西有关的题目主要考虑
pushdown
与pushup
操作。 -
连续区间的维护,想到线段树。
-
线段树二分如果不是从末尾开始的,要
bool flag; int find_res
。e.g CF407E
-
-
Heap / priority_queue
- 如果遇到一些:把一个数划分为数量固定的几个数的和,要求什么最优值,可以考虑先把每个数设为 \(1\),塞进优先队列里按贡献一个一个加。e.g. ABC359F
-
Trie
- 有异或,想到 Trie。
-
DSU
-
图论联通、MST、删边之类的,可以考虑从小到大(单调就行)加边,dsu 维护。e.g. 好多啊…… CF1857G
-
dsu 和
set
都可以维护连续段。
-
-
笛卡尔树
- 笛卡尔树是刻画区间最值的数据结构,本质其实就是区间最值的位置。因此如果题目里有区间最值,可以考虑钦定最值的位置。
-
单调栈
- 单调栈是维护区间后缀最值的结构。e.g. CF407E
Algorithm
-
DP
-
换根 DP
-
常用思考方式:
-
树形 DP 做出定根的答案;
-
假如从 \(u\) 到 \(v\) 换根,那么先把 \(dp_u\) 去掉 \(dp_v\) 的贡献作为 \(v\) 的一个儿子计算。
-
-
-
数位 DP
- \([X, Y]\) 中满足 \(p(n)\) 的 \(n\) 的计数,极其常用的技巧:计算 \(X, Y - 1\) 的答案 \(F(X), F(Y - 1)\),\(ans = F(X) - F(Y - 1)\)。
-
DP 设计 / 思考
-
DP 转移
-
如果转移方程发现有设计当前不能确定的参数、变量之类的,不妨试着将这些东西“拖延”下去,在能确定时再转移。e.g. P6622
-
如果转移太慢,可以预处理优化压掉一点复杂度。e.g. 「联合省选 2020 A | B」信号传递
-
有环的转移可以变成解方程!e.g. 赶小猪
-
如果方程元很多,但是一个元只和少量元相关,那可以尝试主元法!e.g. #6357.
-
如果主元法又是
double
题,那就不要主元法(精度要炸),观察一下这个增广矩阵有什么特殊性简化高斯消元。e.g. CF24D.
-
-
DP 优化
-
状态太复杂时把状态压到 dp 的值离去,最后统计答案。e.g. ABC325F
-
矩阵优化 DP 可以把一些不必要的内容删掉(比如 \(n \times n\) 的图矩阵变成 \(1 \times n\) 的向量)。e.g. 「NOI2020」美食家(还没做 😛
-
矩阵优化 DP 也可以用结合律换掉计算顺序,比如从向量开始乘,而不是从矩阵开始。e.g. 信友队一道远古例题。
-
如果 dp 数组里只有 \(0/1\) 表示存在性的化,转移是很浪费的(枚举一堆没用的 \(0\))。此时可以考虑用个
vector
记录哪里有 \(1\)。e.g. CF1582F2 -
DP 如果是随意选满足条件的 \(n\) 个数的计数的话,可以把 \(dp_{i, x}\) 表示选 \(i\) 个物品变成 \(dp_{w, x}\) 表示选 \(2^w\) 个物品。e.g. P3321
-
-
-
搜索
-
想完 DFS 或 BFS 想想另一种能不能做。
-
思考搜索的状态数,看看用 DFS 还是 BFS。
-
从容易出现矛盾便于剪枝、可能的答案少的地方开始搜索,大幅减小搜索树。example: P1074
-
-
构造
-
发掘问题的性质有助于想出好方案。
-
构造题多想推广法。
-
-
二分
-
可以用逐位确定替代二分,其实就是换成倍增。
-
遇到平均值考虑分数规划。e.g. P5319.
-
-
倍增
- 倍增处理的是一类怎样的问题?进行 \(n\) 步,具有结合律、交换律的信息。因此倍增可以做 LCA、各种二进制分组…… e.g. P3321, 用倍增优化 dp 状态中的选择几个物品
-
分治
- 遇到神秘点对问题可以用分治法做。
-
贪心
-
流水法:大自然是最好的老师。考虑水怎么流比较优?e.g. ARC175C
-
涉及安排顺序的最优化问题考虑贪心,排序关键字邻项交换考虑。之后可能会套个递推 / dp。e.g. XYD0805C, ABC366F, P10811 排序后 bitset 优化
这个算法的本质是我们可以确定选取一个确定集合的顺序 / 哪个是最后一个或第一个,然后按照顺序确定最优集合。
-
Math
-
Number Theory
-
遇到很大的数字求答案可以考虑转成取模意义下的解,多取几个模数最后
CRT
合并答案。 -
把约数存下来非常慢,可以考虑反过来枚举。
-
\([i \bmod 2 = 0] = \frac{1 + (-1)^i}{2}\),\([i \bmod 2 = 1] = \frac{1 - (-1)^i)}{2}\)。
-
有 \(\gcd\) 可以思考 \(\gcd(a, b) = \gcd(a, a + b)\),这样能避免冗余的加法 / 表明 \(\gcd\) 可以套差分。e.g. Interval GCD, CF1322C Instant Noodles
-
-
二进制
-
Linear Algebra
-
Other
-
绝对值可以不用分类讨论,可以把值(比如 \(a\))变成 \(-a\) 再做一遍。
-
维护乘法(像 ARC182C 那样的)可以考虑这个公式:
\[\prod_{i \in S} (t_i + d_i) = \sum_{T \in S} (\prod_{i \in T} t_i \times \prod_{j \in S} a_j) \] -
由类似 \(\lceil n / k \rceil\) 之类的东西可以考虑抽屉原理。
-
数数
- 每个都必须选一个等价于每个都至少选一个,后者可以容斥。e.g. P4336
图论
-
MST
-
建立超级源点向所有点连权值为对应点点权的边,再跑 MST 可以化点权为边权。example: CF1245D
-
按照顺序加边,求第一次满足 XXX 的……先按照时间建 MST!e.g. P10875
-
-
Block forest
- 维护节点信息时,方点只要维护自己儿子的信息即可(是个树嘛,往上跳时顺便就算上贡献了)。但注意一点:如果查询 \(u,v\) 的 LCA 是方点,还要算上方点的父亲。e.g. CF487E
-
Shortest Path
-
Dijkstra,SPFA 可以得到多个源同时的最短路。(最开始把所有源的
dis
设为0
,然后都加进队列) -
最短路的矩阵刻画:
\[ans = G^{n} \times \begin{bmatrix} 0 \\ + \infty \\ + \infty \\ \vdots \\ + \infty \end{bmatrix} \]这里 \(G\) 的矩阵乘法的方式是 \(c_{i, j} = \min_{k = 1}^n a_{i, k} + b_{k, j}\)
矩阵快速幂时,注意单位矩阵应变为除了对角是 \(0\) 其余全部是 \(+\infty\) 的形式。
-
-
基环树
-
先把环拆出来。
-
环可以破环为链。
-
-
匹配
- 最大权独立集:选了 XX 就不能选 XX。
-
网络流
-
如果没有“流守恒”相似的性质,那很可能就不是最大流类问题,得从割或别的角度考虑。e.g. 最大权闭合子图
-
如果是“选择任意个 XX”,可以考虑最小割。e.g. 最大权闭合子图, P2774 方格取数
-
有点权先考虑拆点。e.g. P2045
-
SSP 算法中的最短路具有凸性(斜率递增)
-
-
传递闭包
- 传递闭包是建立边可重与不可重的联系。e.g. 最小路径覆盖
-
网格图
- 可以考虑先黑白染色。
-
LGV
- 有 LGV 的形式但不一定完全是 LGV 的,主要考虑这个容斥系数 \(-1^{\operatorname{sgn}(\sigma)}\)。e.g. ABC216H
-
Other
-
复杂的路径 / 别的东西可能是简单的东西拼起来的。e.g. NOI2019 序列
-
邻接矩阵有时可以看作一种矩阵。
-
虚拟结点是个好东西,非常妙。
-
从小到大加边,很频繁的 trick。
-
分层思想。e.g. #3004
-
树
-
从小到大加边,很频繁的 trick。
-
LCA
- 动态维护 LCA,可以按照 dfs 序排序,dfs 序最小的和最大的点的 LCA 就是整体的 LCA。e.g. P10517
-
直径
-
广义直径:点集最远距离。
-
直径端点可以线段树维护。
-
-
虚树
-
不显示建虚树
现把要求的点按照 DFS 序升序排序。然后把第一个点 - 根的贡献加上去(这是为了写得清楚方便)。这样后求排序后的相邻两点 \(v_{i - 1}, v_i\) 的 lca,把 \(v_i-lca\) 的贡献加上去。最后求所有点的 lca(即 \(lca_{v_1, v_n}\)),把 \(lca -root\) 的贡献删掉。
代码实例
e.g. #2562. 「SDOI2018」战略游戏,关键代码:
int ans(LCA::val[s[1]]); L(i, 2, S) ans += LCA::val[s[i]] - LCA::val[LCA::lca(s[i - 1], s[i])]; printf("%d\n", ans - LCA::val[LCA::f[lca(s[1], s[S])]] - S /* 对于本题有额外的贡献要去除 */);
-
不显示建虚树的另一种方法:
\[ans = \sum d(u_i, lca) - \sum d(lca(u_i, u_i + 1), lca) \](\(u\) 已经按照 dfs 序排序)
e.g. P10517
-
-
Other
String
-
Hash
- 能单模就尽量单模,因为单模不用
umap
这类东西,常数巨小,甚至可以直接bitset
。e.g. CF1063F solution。
- 能单模就尽量单模,因为单模不用
专题
序列区间
-
可以考虑转为差分问题。e.g. CF1120D
-
可以固定一个端点,确定最有左端点。
-
区间操作,可以拆区间。(像 ST 表、二进制拆之类)e.g. 区间平行连边建图。
-
分治是解决序列问题的好方法。
-
扫描线可以解决很多数特殊子串的问题。
-
如果我们要做一个区间扩展问题(比如要求个什么东西,我们发现可以由小 / 非法区间扩展开来(变大)),然而我们不知道到底扩展哪一边。此时可以考虑两边同时扩展,再考虑怎么算之类的问题。e.g. ARC173C
随机化
- 对于一个可以在随机数据下跑得快,但会被卡的算法,可以考虑把计算顺序打乱等方法防止被卡。e.g. XYD0730D
最优化问题
- 如果又要求 \(\max\) 又要求 \(\min\),试着把相反数带进去。这样子写起来快多了,还不容易出错。e.g. #6012. 分配问题
有理有据的乱搞
-
由 \(1 / -1\) 随机组成的序列,前缀和中的最大值的规模是 \(O(\sqrt n)\)。于是跑背包的时候可以先
shuffle
一遍。e.g. LG 2024 秋令营 Day2 B事实上,背包前
shuffle
几下是可能很有用的方法。总之,如果需要构造数据才能卡的做法,先想shuffle
行不行。 -
调整法:先瞎搞出一个解,然后不停尝试枚举所有可行的改变答案的方法,想办法调整。(感觉和模拟退火很像啊)e.g. ABC373G
杂项
-
所有数互不相同 = 排序后递增。e.g. 240805 模拟赛,一个 DP。
-
题目中有 \(\max(a, b)\),可以试图对 \(a, b\) 的大小分讨。
-
想了正向统计就要想反向的。
-
可以用对数和指数函数实现乘法与加法的转化。如果是模意义下的,底数要使用原根保证唯一性。e.g. P3321
-
告诉你要把给每个点选黑 / 白的状态,其实可能不是每个点选,而是叫你选出一个集合来。e.g. UOJ #77.
写代码
浮点数
-
floor
减小精度误差:floow(x + eps)
。e.g. THUPC 2017 -
用了
double
的网络流要用eps
,不然可能玄学死循环。 -
避免
double
的一种方法:值域扩大 \(10/\Delta\) 倍,其中 \(\Delta\) 是题目允许的误差。不过注意不要漏乘。CF925F
STL
- 二维
map
:map < pii, int >
。
其它
- 多测 + 记忆化搜索,可能记忆化可以复用。e.g. P4213 杜教筛,不用清空
map
,discussion link。
调题
-
有思路但写懵逼了?考虑每个变量的定义并画图,或将流程用图画出来思考怎么写。
-
没思路还人类智慧?考虑多玩几组样例。
-
画图!画图!画图!
-
可以对一个题的几个部分写出不同的做法,或者重写一次代码作为对拍的拍子。可以有效防止写挂。
-
size -G ./example
可以测空间,单位是 Byte。 -
-D_GLIBCXX_DEBUG
,-D_GLIBCXX_DEBUG_PEDANTIC
可以在 STL RE 的时候直接报错。(用法:g++ a.cpp -o a -std=c++14 -Wall -O2 -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
) -
开报错警告
-
-Wall
: 标准警告 -
-Wextra
: 额外警告,包括-
unused 变量函数
-
可能会导致安全、性能问题的问题
-
-
-Wshadow
: 内外部变量同名警告 -
-Wpedantic
: more strict. 建议加上防止奇怪问题-
不符合 C++ 标准的代码
-
可能导致 UB 的代码
-
可能导致代码难以维护的代码
-
这里似乎还有很多,就先不记了。
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示