感谢 huangkx 的 trick 转载。
- 用可持久化线段树维护非递归线段树的树链信息可以高效地解决区间半群问题。
- 线段树维护的序列长度要保持不变。
- 关于 \(d\)(约数个数函数):\(d ( n m ) = \sum _ { x \mid n } \sum _ { y \mid m } [ \gcd ( x , y ) = 1 ]\);由此可以推导出当 \(m\) 为质数,\(d ( n m ) = 2 d ( n ) - [ m \mid n ] d ( \frac n m )\)。
- 关于 \(\mu\):\(\sum _ { d | n } \mu ( d ) = [ n = 1 ]\),即 \(\mu \ast 1 = \epsilon\)。莫比乌斯反演题的关键:把 \([ \ldots = 1 ]\) 用 \(\mu\) 表示。
- 关于 \(\varphi\):\(\sum _ { d | n } \varphi ( d ) = n\),即 \(\varphi \ast 1 = id\)。欧拉反演题的关键:把 \(\ldots\) 用 \(\varphi\) 表示。
- 关于 \(\mu\) 和 \(\varphi\):\(\mu \ast id = \varphi\)。
- 莫比乌斯反演题的常用式子:\(\sum _ { i = 1 } ^ n \sum _ { j = 1 } ^ m [ \gcd ( i , j ) = k ] = \sum _ { i = 1 } ^ { \lfloor \frac { n } { k } \rfloor } \sum _ { j = 1 } ^ { \lfloor \frac { m } { k } \rfloor } [ \gcd ( i , j ) = 1 ]\)。
- 当 \(i \times j = k\) 时,可以枚举 \(i , j , k\) 中的任意两个,求出第三个。枚举因数和枚举倍数可以互相转化。
- 网络流建图技巧:《浅谈网络流的各种建模方法》。
- 存储多项式时,\(n\) 代表多项式的项数,而不是最高次项的次数。
- 用 \(\texttt {FFT}\) 求出的答案可能会有精度误差,需要加上一个值,比如 \(0.5\),再向下取整。
- 枚举集合 \(S\) 和它的子集 \(T\)(\(S\) 和 \(T\) 都不为空):
for(int S = 1; S < (1 << n); S ++) for(int T = S; T != 0; T = (T - 1) & S)
。这段代码的时间复杂度为 \(\mathcal { O } ( 3 ^ n )\)。 - 某些情况下,对于奇偶性的分类讨论可以用 \(- 1\) 的幂代替。
- 用树形背包时,处理完以当前结点的某个子结点为根的子树后,再将这棵子树的大小加到以当前结点为根的子树的大小里。
- 用树形背包时,可以用一个临时数组记录当前结点上一次的 \(\texttt {DP}\) 状态,以避免状态转移顺序的问题。
- 某些情况下,二分时可以把集合中的数变为 \(0\) 和 \(1\)。
- 把一个集合分裂为若干集合可以看成把若干集合合并为一个集合。
- 博弈论中,可以找到两类状态 \(A\) 和 \(B\),使得先手可以把 \(A\) 变为 \(B\),后手一定会把 \(B\) 变为 \(A\),这样就能确定先手必胜和必败的情况。
- 矩阵上的动态规划中,若可以删除一行或一列后再把两边合并,则可以设 \(f _ { i , j }\) 表示用了 \(i\) 行 \(j\) 列的答案。
- 当边权很小时,可以把每条边拆成若干条边权为 \(1\) 的边。
- 善用
__gcd()
和__lg()
。 - 当 \(x\) 为 \(\texttt {unsigned int}\) 类型时,
__builtin_popcount(x)
表示 \(x\) 的二进制表示中 \(1\) 的个数,__builtin_clz(x)
表示 \(x\) 的二进制表示中前缀 \(0\) 的长度,__builtin_ctz(x)
表示 \(x\) 的二进制表示中后缀 \(0\) 的长度,31 - __builtin_clz(x)
表示__lg(x)
,即 \(\lfloor \log _ 2 x \rfloor\);当 \(x\) 为 \(\texttt {unsigned long long}\) 类型时,__builtin_popcountll(x)
表示 \(x\) 的二进制表示中 \(1\) 的个数,__builtin_clzll(x)
表示 \(x\) 的二进制表示中前缀 \(0\) 的长度,__builtin_ctzll(x)
表示 \(x\) 的二进制表示中后缀 \(0\) 的长度,63 - __builtin_clzll(x)
表示__lg(x)
,即 \(\lfloor \log _ 2 x \rfloor\)。 - 定义 \(\texttt {DP}\) 状态时,可以把 \(f _ i = j\) 中的 \(i\) 和 \(j\) 互换,以解决值域限制的问题。
- 难以统计满足所有条件的情况数,但容易统计不满足所有条件的情况数的计数题可以用多步容斥解决。
- 从低位到高位建立的 \(\texttt {01-Trie}\) 可以通过交换左右子树并向下递归来实现全局 \(+ 1\) 操作。
- 已知 \(a , b , p\),则 \(( a + b c ) \bmod p\) 的最小值为 \(a \bmod \gcd ( b , p )\)。
- 当每个数值只出现一次时,一些数的 \(\operatorname {mex}\) 相当于除它们以外的数的 \(\min\)。
- 当每个数值只出现一次时,若一些数的个数等于它们的 \(\max - \min\),则这些数的数值是连续的(排序后为一段公差为 \(1\) 的等差数列)。
- 若两个集合中的数值都是连续的,且每个集合中每个数值只出现一次,则它们的交的数值也是连续的。
- \(\operatorname { and }\) 和 \(\gcd\) 可以互相转化;\(\operatorname { or }\) 和 \(\operatorname { lcm }\) 可以互相转化。
- \({ m \choose n } \bmod 2 = [ m \operatorname { and } n = n ]\),可以用 \(\texttt {Lucas}\) 定理证明。
- 多个结点的最近公共祖先为它们在 \(\texttt {DFS}\) 序上编号最小的结点和编号最大的结点的最近公共祖先。
- \(\gcd ( f _ m , f _ n ) = f _ { \gcd ( m , n ) }\),其中 \(f\) 表示斐波那契数列。
- 注意质数的奇偶性。
- 颜色段均摊(珂朵莉树)的映射思想的拓展:《 ODT 的映射思想的推广 》。
- 在线莫队:《你以为莫队只能离线?莫队的在线化改造》。
- 无修改在线莫队的特征区间可以只记录向两边延伸时可能影响答案的数值。
- 颜色段均摊(珂朵莉树)中的每个颜色段可以直接在添加和删除时计算贡献。
- 可以用颜色段均摊(珂朵莉树)和在线莫队解决带有区间覆盖操作且询问难以用常规数据结构处理的数据结构题。具体地,对于每个颜色段,直接更新它对每个特征区间的贡献。
- 在某种情况下重构数据结构可以保证它的一些优秀性质,如替罪羊树的思想;或者每经过若干次操作就重构整个数据结构,如朝鲜树的思想。
- 对于询问寻找对应的答案和对于答案寻找对应的询问可以互相转化。
- 与奇偶性有关的问题可以转化为与异或有关的问题。
- 数据范围小的问题可以考虑状态压缩。
- 奇排列是标准排列被某个奇置换作用后的排列,可以与标准排列通过奇数个对换互变;偶排列是标准排列被某个偶置换作用后的排列,可以与标准排列通过偶数个对换互变。
- 对于与 \(\gcd\) 相关的问题,可以把 \(a\) 和 \(b\) 除以 \(\gcd ( a , b )\) 来分析。
- \(\gcd ( a , b ) = \gcd ( a , \mid a - b \mid )\)。
- \(\operatorname { lcm } ( a , b ) = \frac { a b } { \gcd ( a , b ) }\)。
- 序列逐渐扩大或逐渐缩小的题可以考虑区间 \(\texttt {DP}\)。
- 遇到乘法要考虑负负得正,有时需要同时维护最小值和最大值。
- \(\texttt {DP}\) 转移时采用最近的状态。
- 对于某些求最大贡献的题,若可以对元素取相反值来撤销其贡献,则可以进行后悔贪心,每次选择贡献最大的一些元素,但需要在选择后把选择的元素变成其相反值。如:HDU1024 Max Sum Plus Plus 和 CF280D k-Maximum Subsequence Sum。
- 用链式前向星存无向图时,存边的数组的大小应为边数的两倍。
- 可以建反图来求所有点到某个点的最短路。
- 询问最小的把给出的正整数序列 \(a _ 1 , a _ 2 , \ldots , a _ n\) 中的某些数加起来不能表示出的正整数,可将 \(a\) 排序后先判断 \(a _ 1 = 1\) 是否成立,若不成立则答案为 \(1\),若成立则再按 \(i\) 从小到大判断 \(1 + \sum _ { j = 1 } ^ { i - 1 } a _ j \geq a _ i\) 是否成立,若不成立则答案为 \(1 + \sum _ { j = 1 } ^ { i - 1 } a _ j\),若仍得不到答案,则答案为 \(1 + \sum _ { i = 1 } ^ n a _ i\)。因为只要上述条件都成立,就可以用 \(a _ 1 , a _ 2 , \ldots , a _ i\) 表示出 \(1 , 2 , \ldots , \sum _ { j = 1 } ^ i a _ j\),这个结论可以用归纳法来证明。
- 当 \(a\) 不变时,\(\lfloor \frac a i \rfloor\) 是随 \(i\) 单调递增的;但是当 \(a , b\) 不变时,\(\lfloor \frac a i \rfloor - \lfloor \frac b i \rfloor\) 不是随 \(i\) 单调递增的。
- 有时用减法代替取模速度会更快,这个技巧可用于卡常。
- \(\operatorname { atan }\) 函数的返回值是弧度。
- \(x \bmod a = x \bmod b = x \bmod \operatorname { lcm } ( a , b ) \bmod a = x \bmod \operatorname { lcm } ( a , b ) \bmod b\),若 \(\operatorname { lcm } ( a , b )\) 不变,可先模它来缩小取值范围,如 CF55D Beautiful numbers。
- \(( a \mid c ) \land ( b \mid c )\) 为真的充要条件为 \(\operatorname { lcm } ( a , b ) \mid c\) 为真。
- 要用数位 \(\text {DP}\) 的题目常询问 \([ l , r ]\) 中满足某条件的数的个数,一般要把它拆成 \([ 0 , r ]\) 中满足某条件的数的个数减去 \([ 0 , l - 1 ]\) 中满足某条件的数的个数。
- 数位 \(\text {DP}\) 的状态中一般要加一维(\(0\) 或 \(1\))表示当前选出的前缀是否与最大值(上限)的同样长度的前缀相同,用来求出当前位的取值范围。
- 数位 \(\text {DP}\) 的具体实现方法一般为记忆化搜索,即从高位到低位填数字,从低位到高位更新答案。记忆化搜索一般只存储不顶上限的情况,因为不同询问给出的上限不同。
- 每种情况得到的元素的个数之和可以转化为每个元素造成的贡献之和,如 51Nod-1677。若元素有权值同理。它的本质为改变贡献的主体。
- 一个数大于等于它的每一位上的数字的乘积,如 BZOJ 2713 [Violet 2] 愚蠢的副官 & BZOJ 1183 [Croatian 2008] Umnozak。
- 正负数 \(\operatorname { << } 1\) 都相当于乘 \(2\),\(\operatorname { >> } 1\) 都相当于除以 \(2\) 并向下取整。
- 正负数 \(/ 2\) 都相当于除以 \(2\) 并对绝对值向下取整,即正数向下取整,负数向上取整。
- \([ { m \choose n } \bmod p \geq 1 ] = [ \text {在 } p \text { 进制下,} m \text { 的每一位都大于等于 } n \text { 的对应位} ]\)。可以用 \(\text {Lucas}\) 定理证明。