Tricks

  • 字符串
  1. 计算一个字符串 S 的 border 的时间复杂度是 O(|S|) 的,且与模板串无关。在更换模板串时,不需要重新计算 border。对于两个字符串集合,两两匹配的时间复杂度从 m|S|+n|T| ,降低到了 |S|+n|T|。(2.16 A 60pts)
  • 动态规划
  1. 有些题目可以先考虑贪心策略,再在贪心策略的基础上 dp。例如你发现最优策略一定满足选择的是一段区间(但是不知道是哪个区间),就可以使用 dp。(2.17 B 50pts)

  2. 有些图论问题,最后的答案只与其中的一些量有关(比如 距离),那你在 O(mlogm) 的时间复杂度内求出 距离,这道题就和图没有关系了。

  3. 有些题目由多个并列约束组成,最后的方案要满足所有约束,一般是确定一个序列,且序列的前缀要满足某些条件,一般是最优化问题。这种问题可以把约束放在坐标系的折线上,考虑折线上的转移关系。

  4. 对于一些局限影响的题目,先考虑影响周期。例如一个排列有几个条件,条件本身只基于四个东西的顺序,那你实际的顺序个数只有 4! 种,可以方便 dp。

  5. 有一些数据范围不好用 dfs 枚举,但是也是指数级别的,考虑用状压 dp(这个过程可以用 dfs 枚举来转移)。

  • 贪心
  1. 如果一个决策你人类思维下是对的,而且可以过样例,就去写写试试。就比如说,有多个优先级相同的事件,ddl 不同,你肯定优先做 ddl 靠前的任务。
  • 数据结构
  1. 敏感发现 单调性 和 不可逆性:如果一个东西的状态 S 转移到 S 后就再也无法转移回 S,那可以考虑时间轴上做数据结构。(2.16 C 85pts)

  2. 只跟排名有关的带修题目,可以通过树状数组维护,典型例子是逆序对。你可能需要离散化,或者将树状数组改成 map 实现(2.17 C)

  3. 用数据结构维护带修哈希。

  • 杂项
  1. 对于若干个区间 [l1,r1],[l2,r2],,[ln,rn] 的交,答案是 [maxi=1nli,mini=1nri](如果区间不合法则无交集)(2.16 C 50pts)

  2. 对于一个题目,如果出现了本应该同阶,但实际上出现了(哪怕是部分分)不同阶的情况,考虑离散化。(2.16 C 65pts)

  3. 双集合题,考虑将其中某一个集合 S 约束为 |S|=1 后怎么做。以及,这个集合 S 中的贡献关系是什么。(2.17 A)

  4. 定长区间问题,可以通过增量式修改减小常数,在 bitset 硬莽题有用。

  5. 枚举子串问题,尝试通过约束子串长度降低复杂度。如果只有 len10 的串会产生贡献,则时间复杂度就是 O(10n) 级别的。(2.17 C Sub 6、7)

  6. 如果你能在搜索时,将目前搜索的状态唯一的表示出来,而这个状态可以转移到另外一个状态,就相当于在 dp 里的转移关系。
    例如在 2.19 A 的 40pts 做法中:我们的 dfs 内容可以用唯一的四元组 (n,k,t,l) 表示出来。分别表示 将 n 分成 k 份,使得最长的一段是长度为 l 的方案数,目前的这一段长度是 t。对于一个 (n,k,t,l) ,他代表的情况是唯一的。然后你枚举对于新的数 n,是切段另起一段还是不切。对于每种情况你有 2 种选择,看似时间复杂度是指数的,但实际上因为每一个转移情况只会被算一次,所以时间复杂度是四元组 (n,k,t,l) 的数量,也就是 O(n4)

然后其实,你在 1s 之间内可以计算至多 3×108 次搜索,就算把结果全部记录下来也就是 1144 MB,反正你不可能全部存下来,所以写 dfs 搜索的情况下,可以全部记忆化。

然后还有一种更加不优美的,就是对于一个状态你可以对他状压,就比如说最后状压大小是 998244353,然后每个状态依赖的一定在这 998244353 种情况里,最优化题目,哈希冲突的情况正好在最优化路径中的概率非常小。

  1. 有些题目的基本变量被赋值为 0 时,基本一定有简单做法(不然这个变量没啥意义)。这个 Subtask 就算被排列在比较靠后的位置,也要考虑去完成。如果人类思维无法理解这个 Sub 怎么做,可以考虑打完暴力找规律。

  2. 有些题目数学意义上的答案应当是个整数(是个浮点数),但是允许给出接近的浮点数(整数)答案。这种过程是丢失精度的,说明这道题需要一些不确定做法。然后,一眼丁真的不可做题也可以当作是随机化题做。

  3. 如果转移是两个 bool 数组的运算,那可以通过 bitset 使得时间复杂度降低到 O(nw)。然后,在 dfs 里面,bitset 维度数越低越好;同时,如果可以钦定一些答案必然是相同的,就可以让常数减小一些。

  4. 学会读样例文件,你发现一个答案上界是 1012 的题,实际答案只有几十几百个,你就可以思考答案为什么这么小,和随机数据/弱数据的性质是否有关系。

  5. 如果是关于子集的题目,而且子集之间存在贡献。那么这道题就可以理解为维护 S 所有子集的子集 这样的题目,可以通过下列算法在 O(3n) (而不是 O(4n))的时间复杂度里计算。

for (int m = 0; m < (1 << n); ++m)
  for (int s = m; s; s = (s - 1) & m)

证明:对于每一位只有 s,m 都有,sm 有,s,m 都无 三种情况。

  1. 如果你发现满足要求的子集一定满足某一个要求,那么上文的 O(3n) 就可以转化成 O(n×2n)

  2. 一些要求连续性的序列,随机情况下连续长度是 log 级别的,所以可以有一些基于连续长度的做法。

posted @   wtcqwq  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示