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。如果数据有点弱,可以骗很多分。

数据结构

  • 线段树

    • 线段树维护奇怪东西有关的题目主要考虑 pushdownpushup 操作。

    • 连续区间的维护,想到线段树。

    • 线段树二分如果不是从末尾开始的,要 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

      • 常用思考方式:

        1. 树形 DP 做出定根的答案;

        2. 假如从 \(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 可以大胆设包含很多信息的状态,凭经验,用到的状态不会很多,map 记录即可。e.g. 笔刷, CF900D.

      • 对于计数 DP / 几乎所有 DP,思考你的目标是什么,这点很重要。如果你算的是一个部分,考虑整体结果可不可得。e.g. CF900D

      • DP 的本质是一张 DAG。因此如果 DP 不是普通的起点固定找终点的话(终点固定找起点),可以倒过来 DP。e.g. LG 2024 秋令营 Day2 C

    • 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

  • 二进制

    • 有异或,想到 Trie。e.g. CF888G

    • 有位运算通常拆位考虑。有加法的拆位方法是第 \(k\) 位只能被前 \(k\) 位影响,然后可以试图在模 \(2^{k + 1}\) 的意义下考虑。e.g. CF1322B

  • Linear Algebra

    • Gauss Elimination

      • 对于图上一些不确定的值的问题,可以建出线性方程(等量关系),用高斯消元求解。e.g. 赶小猪
    • 线性代数有很大的对称性 / 均匀性。比如一堆 \(0/1\) 那所有子集的异或和肯定是 \(0, 1\) 各半。线性基能异或出的数出现次数一样。e.g. CF1336E1, UOJ36

    • 很长的 \(01\) 向量可以对于每一维随机赋权。e.g. P10778.

  • 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 算法中的最短路具有凸性(斜率递增)

  • 传递闭包

  • 网格图

    • 可以考虑先黑白染色。
  • 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

    • 树上信息本身不好维护,可以通过 DFS 序转化为序列维护。

    • 建立虚拟节点把森林连成一棵树。

    • 如果题目与树的度数有关,那么树的总度数是确定的(\(2n-2\)),可以以此转化为序列分配问题。e.g. ABC359F

    • 从哪里来的本质可能是一个树状结构,可以考虑状态之间如何构成一个抽象关系。e.g. CF1271E.

    • 经典转化:节点对的贡献变成 LCA 的贡献。

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

  • 二维 mapmap < pii, int >

其它

调题

  • 有思路但写懵逼了?考虑每个变量的定义并画图,或将流程用图画出来思考怎么写。

  • 没思路还人类智慧?考虑多玩几组样例

  • 画图画图画图

  • 可以对一个题的几个部分写出不同的做法,或者重写一次代码作为对拍的拍子。可以有效防止写挂。

  • 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 的代码

      • 可能导致代码难以维护的代码

    这里似乎还有很多,就先不记了。

posted @   sihiazi  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示