常见技巧总结
1 动态规划
- 对于贡献存在于整棵树上的 dp,考虑分两次分别计算子树内和子树外的贡献。也就是传统的 up and down。
- AC 自动机上 dp 一般考虑设状态为:\(dp(i,j)\) 表示枚举到 AC 自动机上第 \(i\) 个点,字符串长度为 \(j\) 时的方案数。在 \(n\) 较小的时候也可以再加上一维状压。
- 当 dp 题目中出现差值的时候,可以考虑差值 dp。具体的,状态中设一维 \(x\),dp 值记录与 \(x\) 的差值,答案就是 \(x+dp(x)\)。
- 灵活运用刷表法和填表法进行转移。
- 状压 dp 时,
for (int T = S; T; T = (T - 1) & S)
可以不重不漏枚举 S 二进制下的所有子集。
- 在区间 dp 时,单次考虑取一个或者取一段区间所得出的方程不同,最后做法的难易程度也可能不同,需要灵活处理。
- 树形 dp 时,我们可以考虑对于每一条重链只开一个 dp 数组进行转移,在重链头进行合并,可以将空间复杂度优化至 \(\log\)。
- 在 dp 状态中,如果有一维的状态很大而难以记录,考虑矩阵快速幂优化,或者去发现一些特殊性质以优化状态设计。
- 在 dp 转移中,如果出现两维需要一起转移,然而两维实际上是独立的时候,可以考虑分步转移以优化复杂度。
- 对于配对问题,一种经典做法就是记录前面有多少个位置还没有配上,进而计算贡献。
- 对于一些题目,其操作正向考虑较难计算贡献,我们可以反向考虑最后一次操作的贡献。
2 图论
- 对于边权为 \(1\) 的图,常采用 Bfs 求解各种问题。
- 树上 \(n\) 个关键点之间形成的最小联通子图的大小为 \(\sum\limits_{i=1}^n \operatorname{dep}_{a_i}-\sum\limits_{i=1}^{n-1}\operatorname{dep}_{\operatorname{lca}(a_i,a_i+1)}-{\operatorname{dep}_{\operatorname{lca}(a_1,a_2,a_3,\cdots,a_n)}}\)(按 DFS 序排序后)。
- 一个森林的连通块个数是 点数 - 边数。
- 在树上维护路径的题目中,需要考虑路径可以拆成哪几段路径以更方便的处理。最常考虑的就是 \(\text{LCA}\) 以及根节点。
- 对于求一些路径总和的题目,可以考虑拆成每一条边的贡献。
- 图上有一个经典容斥套路:假如要求 \(n\) 个点的图满足某一条件且联通,设 \(f(i)\) 表示 \(i\) 个节点的满足条件且联通的图的方案数,\(g(i)\) 表示 \(i\) 个节点的满足条件的图的方案数。如果可以简单的求出 \(g(i)\),就可以用 \(g(i)\) 求出 \(f(i)\) 了。具体的,对于一个 \(f(i)\),用其方案数应该是 \(g(i)\) 减去不联通的图的方案数。我们钦定不联通的图中,\(1\) 号点所在连通块大小为 \(j\),则此时不合法方案数就是 \(f(j)g(i-j)\binom{i-1}{j-1}\)。
- 树上启发式合并的复杂度很优秀,关键的时候需要想能否通过启发式合并优化复杂度
3 二进制 / 位运算
- 几个二进制数之间位运算最优性问题可以考虑 0-1 Trie。
- 对于某些二进制问题可以考虑将每一位拆开计算。
4 数据结构
- 序列上进行操作的题目,如果不像传统的数据结构题,可以先去发现一些性质,然后可能会发现答案与某一数组的求解关系较大,而这个数组的求解就可能用到数据结构。
- 当某一操作或某一贡献只在特定区间内存在时,考虑线段树分治。
- 可以在平衡树上的每一个节点维护一个区间,当需要查询其子区间信息的时候将该节点分裂即可。
- 假如要维护的东西不是一个数值而必须维护一些数组,考虑减小其规模,这样仍可以在线段树上暴力合并求解。
- 二分加线段树的复杂度是两个老哥,由于线段树的大常数这样的做法其实往往难以通过。考虑直接在线段树上二分来替代。
- 序列上的问题除了考虑传统数据结构外,也可以考虑在前缀和 / 差分数组上操作,或许会发现更为优秀的性质。
- 线段树懒标记的本质是记录不同时间对该区间的操作。因此先下放的懒标记的操作时间一定是小于当前的懒标记的。可以通过这个性质维护一些有时间限制的操作。
- 注意当操作看上去较为难维护的时候能不能通过离线 / 扫描线等方式简化。
5 其它算法
5.1 贪心
- 求解贪心最简单的做法就是考虑邻项交换法,以此算出排序函数。
- 需要注意的是排序函数需要满足四个性质:非自反性、非对称性、传递性、非可比性的传递性。如果不满足需要考虑怎样修正。
- 传统贪心无法解决的问题可以考虑反悔贪心。具体的就是用一个堆去维护已经做过的决策,如果决策放到现在做更有就更换。
5.2 倍增
- 在序列上按照同一种方式走的时候,可以考虑倍增预处理辅助求解。
5.3 中位数
- 传统求解区间中位数的方式是对顶堆,即维护两个大小相差不超过 \(1\) 的堆,插入时动态调整即可。利用
set
就可以支持删除操作。
- 数据随机的情况下,每一次增加一个数对中位数的影响是常数级的,于是可以直接暴力。
6 数学
6.1 组合数学
- 在题目中要求某一个值的 \(k\) 次方之和时,我们常考虑 dp,设状态为 \(dp(i)\) 表示值的 \(i\) 次方之和,然后根据二项式定理进行合并即可。
- 和式有四大变换技巧需要注意:交换枚举顺序、改变指标变量、替换条件式、分离变量。如有大量求和式时考虑用如上方式优化复杂度。
- 对于一些组合数求和,可以从组合意义上考虑等价的表达,以优化时间复杂度。
- 对于一些所求答案形式较为奇怪的计数题,考虑能否赋予其组合意义以辅助求解。
- 在计算答案的过程中,如果发现难以保证最后的目标值恰好等于题目要求,但是可以保证这个值至少等于某个数,可以考虑设 \(g(i)\) 表示目标值至少为 \(i\) 的方案数,利用二项式反演来得出最后的答案 \(f(i)\)。
6.2 概率和期望
- 对于计数题,有时可以转化为答案的期望乘上方案数,这样可能会有更好的性质。
- 对于图上求期望 / 概率,是 DAG 的可以拓扑排序 dp,非 DAG 的可以考虑高斯消元。
6.3 数论
- 题目中要求解一些数字的 \(\gcd,\text{lcm}\) 为固定值时,考虑经典的 \(\gcd\) 容斥。设 \(f(x)\) 表示算出的 \(\gcd\) 为目标值 \(x\) 的倍数的方案数,利用莫比乌斯反演求出原函数值。
- 对于大量相同形式的求和,如果时间复杂度不支持暴力计算,考虑仿照快速幂的方式倍增求解。
- 整数的质因数分解式不仅仅用于传统数论题,它也用在估计答案上界、优化状态记录等。
7 杂项
- 多次多个元素的贡献可以拆成多次单个元素贡献的和,即考虑每一个个体对整体的贡献。
- 对于判断可以或不可以的题目,可以先看可以或不可以时满足的条件,以此发现一些性质后去做。
- 当出现
至少一个
等字眼时,考虑用总的情况减去全部非法的情况。
- 正序做困难时考虑反向做(包括但不限于组合数学、序列问题、图上删边等问题)。
- 遇到取整的时候不妨考虑 \(\pm0.5\)。
- 特殊情况下可以考虑数据随机时更暴力的解法。
- 对于某些序列问题,可以考虑能否通过连边将其转化为树上问题求解。
- 当题目中出现区间 \(\max,\min\) 的要求的时候,常见处理方式有两种:单调栈去算一个数对区间的贡献,或者笛卡尔树固定一个区间的最值算贡献。两者的本质都是固定一个最值算贡献。
- 当题目的要求有非常显然的必要条件的时候,可以考虑这个条件是否充分。如果不充分但是有一定的充分性,可以考虑随机赋权值以增加其充分的概率来求解问题。
posted @
2024-02-27 19:10
UKE_Automation
阅读(
59)
评论()
编辑
收藏
举报