奇怪的东西合集

开个坑,记一点平时遇到的奇怪的东西(包括但不限于易错点和各种技巧)。


数据结构

  1. 摩尔投票求区间绝对众数:

    区间绝对众数定义为区间中出现次数严格大于一半的数。考虑这样一个过程,每次删除区间两个不同元素,若存在区间众数,则最后剩下的数为区间绝对众数,可用线段树维护投票结果;用平衡树维护每种数字的出现位置,即可检验所剩数字是否为区间绝对众数。

    另:不维护投票结果,在询问区间中随机找 k 次元素并判断,若存在区间绝对众数,则未找到的概率为 12k。见 P3765

  2. 懒标记是延迟对子节点信息的更新,而不是本身。

  3. Splay 修改序列信息时(包括序列的插入、删除和其他修改),中序遍历为序列顺序,对于修改 [L,R],将 L1 对应的节点旋转到根,再将 R+1 对应的节点旋转到 L1 下方,成为 L1 的右儿子,此时 R+1 的左子树即为 [L,R]

  4. 求路径边权信息(和 / 异或和 / min / max 等)可转化为求点权。具体地,对于边 (u,v,w),在 u,v 间加入一个点权为 w 的点。

  5. 树状数组实现区间加 + 区间和:对于原序列 a 的修改 (L,R,val),等价于对差分序列 b 的单点修改 (L,val)(R+1,val);对于 a 的查询 [L,R],进行差分,有:

    i=1xai=i=1xj=1ibj=i=1x(xi+1)bi=(x+1)i=1xbii=1xibi

    于是我们再维护一个满足 ci=ibi 的序列 c 即可。

  6. 树状数组 O(n) 建树:由于节点 x 所管辖的区间为 [xlowbit(x)+1,x],提前维护一个原序列的前缀和即可。

  7. 二维树状数组:

    对于单点加 + 矩阵和,开一个二维树状数组,查询和修改类似一维的情形,做一个循环嵌套,维护矩形的前缀和。类似地,对于矩阵加 + 单点值,二维差分即可。

    对于矩阵加 + 矩阵和,同样仿照一维,考虑矩阵 a 的差分矩阵 b,有:

    i=1xj=1yk=1il=1jbk,l=i=1xj=1y(xi+1)(yj+1)bi,j=(x+1)(y+1)i=1xj=1ybi,j(y+1)i=1xj=1yibi,j(x+1)i=1xj=1yjbi,j+i=1xj=1yijbi,j

    所以用四个二维树状数组分别维护 bi,j,ibi,j,jbi,j,ijbi,j 即可,见 P4514

  8. 线段树注意修改和询问的区间是否合法,存在不合法情况要判 L>R||L<1||R>n,注意要判操作区间,而不是节点覆盖的区间。

  9. 分块在线区间众数:

    块长设为 B,离散化。预处理 fi,j 表示第 i 个块到第 j 个块这个区间的众数的出现次数。具体来说,用一个 cnt 数组作为桶,存各数的出现次数,枚举 i,j,分别是最左侧块的编号以及要移动的右端点指针,jlin 遍历,每次对桶修改,当 j 为某个块的右端点时更新 fi,idj 的值。i 变化时,清空 cnt 数组。

    整块直接用处理出的 f。考虑两侧散块。为了完成这个操作,我们用一个 vector 记录各元素出现的位置 pos,再用一个数组记录元素是所在的 vector 内的第几个元素(即它是序列中相同元素的第几个)。

    对于散块元素 ai,我们考虑它是否会使当前答案 res 加上 1。若 ai 为左侧的元素,在相应 vector 内找到下标为 findai+res 的元素 p,即从它开始向右数第 res+1 个相同元素的下标,pRresres+1;若在右侧,则找 findaires,判断条件为 pL。单次 O(B)。块长取 B=O(nm) 时,时间复杂度为 O(nm),空间复杂度为 O(n+m)。见 P5048


DP

  1. DP 用到的数组注意预处理,包括边界值和非边界的初始化,特别注意最小化答案的题目预先置为 +,预处理容易因为范围原因出错。

  2. DP 转移过程中需要用覆盖的方式更新答案的,要考虑是否需要把之前求出答案存下来,例如 CF581F;此外要注意转移顺序、枚举顺序等,常见的例子是各类背包,例如 P1941

  3. 由状态内情况过多引起的无法转移通常有两种解决方案,增加状态数和对状态限制。

  4. 线段限制类问题按右端点排序,只在右端点处算贡献,以做到不重不漏,例如 AT-DPW

  5. 集合划分类问题通常有一步贪心(以及其他转化),使得最优情况下每组内的元素一定位于排序后的连续的一段,于是可以将集合划分转为序列划分,然后 DP。

  6. 斜率优化:

    • ki,xj 均单调时,用单调队列维护凸包;
    • ki 不单调而 xj 单调时,用单调栈维护凸包,在凸包上二分;
    • xj 不单调时,使用 CDQ 分治。

    xj 不是严格单调时,计算斜率要特判相等情况,下凸壳置为 +,上凸壳置为


字符串

  1. 不同子串个数:

    子串集合与所有后缀的所有前缀构成的集合相同,后缀排序处理出 height 数组,那么字典序顺序下的第 i 个后缀造成的贡献为其长度减去 height[i]。证明:前 height[i] 个前缀一定出现过,而后面的前缀的字典序一定严格大于之前计算过的所有前缀。见 P2408

  2. 在线求模式串在文本串中的出现次数:

    对文本串 S 后缀排序,模式串 TS 中出现当且仅当它是一些后缀的前缀,直接在 sa 数组上做两次二分,二分时比较 S 和一个后缀的字典序大小,复杂度 O(|T|log|S|)

  3. 比较子串字典序:

    对于 S 的子串 A=S[l1..r1]B=S[l2..r2],若 LCP(l1,l2)min(|A|,|B|),则 A<B|A|<|B|,否则 A<Brk[l1]<rk[l2]

  4. 多次做后缀排序需要清空 rk 数组、y 数组和 cnt 数组。


数学

  1. 预处理出 O(n) 范围的质数后,对 n 分解质因数的复杂度是 O(nlnn) 的。

  2. n 做唯一分解后,求出各因数的积性函数值是 O(d(n)) 的。

  3. d(n) 的数量级大致在 O(n13)

  4. n 个数的线性基大小为 s,则能构成 2s 个数,每个数有 2ns 种构造方案。

  5. 概率与期望:

    • E(X)=iiP(X=i)
    • E(aX+bY)=aE(X)+bE(Y)
    • E(X)=i=1P(Xi),这是由于 P(X=i)=P(Xi)P(Xi+1)
    • 概率为 p 的事件,期望 1p 次第一次发生。
  6. minmax 容斥:

    对于满足全序关系且元素满足可加减性的序列 {xn},令 S={1,2,,n},有:

    miniSxi=TS(1)|T|1maxjTxj

    该式意义在于期望下仍成立,即:

    E(miniSxi)=TS(1)|T|1E(maxjTxj)

    此外,有更强的第 k 小形式:

    kthminiSxi=TS(1)|T|k(|T|1k1)maxjTxj

    E(kthminiSxi)=TS(1)|T|k(|T|1k1)E(maxjTxj)

    以上交换 minmax 仍成立。

  7. 狄利克雷卷积中,可以认为一个函数卷 1 就是做前缀和,卷 μ 就是做差分,有以下关系:

    μμ1εμ11μ1d

    φμ1idμ1σ

  8. 幂数列前缀和:

    • i=1ni=n(n+1)2
    • i=1ni2=n(n+1)(2n+1)6
    • i=1ni3=(i=1ni)2=[n(n+1)2]2
  9. 高维前缀和:

    设每一维大小均为 n,共 k 维,直接对已有的结果做容斥是 O(2knk) 的。以二维前缀和为例,当然可以先做每一行,再做每一列,三维类似。那么可以做 k 次一维前缀和,时间复杂度 O(nkk)

  10. 狄利克雷前缀和 / 后缀和 / 前缀逆 / 后缀逆:

    对于数论函数 f,g,若 f(n)=d|ng(d) 则称 fg 的狄利克雷前缀和,gf 的狄利克雷前缀逆;若 f(n)=n|kg(k) 则称 fg 的狄利克雷后缀和,gf 的狄利克雷后缀逆。

    可以 O(nlnn) 调和级数求解,但有更优秀的做法。将每个质因子看作一维,那么每个正整数的函数值可以看作按照质因子进行维度拆分下的高维前缀和,于是效仿高维前缀和的处理方式,枚举每个质因子再累加,时间复杂度同埃氏筛,为 O(nlnlnn)

  11. 高斯消元:

    • 模板:枚举行,行内找系数绝对值最大的作为主元(减小精度误差),找到系数不为 0 的主元则正常对其他行做消元;当该行系数均为 0 时,若常数项不为 0 则无解,否则跳过该行。消元完成后,若之前存在某行系数均为 0 则有无穷多组解,否则有解。

图论

  1. 竞赛图:

    竞赛图定义为对于任意两点 u,v 存在且仅存在一条有向边的有向图(无向完全图对每一条边定向),具有以下性质:

    • 竞赛图缩点后的 DAG 呈链状;
    • 竞赛图一定存在哈密顿路;
    • 竞赛图中的任意一个 SCC 中均存在哈密顿回路。
  2. 二分图:

    • 最小点覆盖 = 最大匹配;
    • 最大独立集 =n 最大匹配;
    • 最小边覆盖 =n 最大匹配。
  3. 强连通分量缩点后为 DAG。

  4. 边双缩点后为一棵树。

  5. 无向图处理点双后建一个新图,原图中的点作为圆点,与代表其所在点双的方点连边,得到圆方树。圆方树具有以下性质:

    • 叶子为圆点,圆点与方点交替连接;
    • 原图两点在同一个点双内等价于圆方树上存在一个方点连接这两点;
    • 被多个方点所连接的圆点对应原图的割点,是这几个方点代表的点双的交点;
    • 原图中任意两点间路径上的必经点是圆方树上这两点路径上的圆点。
  6. 无向图处理点双后,将割点从点双中分离出来,与所在点双代表的点连边,得到块割树,块割树在形态上等同圆方树删去所有叶子,性质与圆方树相似。

  7. 对于一个 DAG,DFS 的出栈顺序为拓扑序倒序。

  8. 无向图上,uv 的所有路径中,不经过重边的路径的边集并是边双树上两点路径上的割边和边双内的边,不经过重点的路径的点集并是圆方树上两点路径上的方点直接相连的圆点。

  9. 最小生成树:

    • 最小生成树是瓶颈生成树的充分不必要条件;
    • 最小生成树上的路径均为最小瓶颈路,但最小瓶颈路不一定都是某棵最小生成树上的简单路径。
  10. 差分约束所得的特解是满足限制的最紧的一组解(最大解)。设超级源点 s 与其他点连边的边权为 ws,有 s 与图中点 u 的关系 disudiss+ws,则有 maxuVdisu=ws。这是由最短路算法的性质决定的,由于每条边是一个限制,最短路保证了限制最紧(例如 disvdisu+w,松弛过程中取到等号,因此使得 disv 在符合限制的情况下最大化)。见 P5960CF1450E


STL 与各类库函数

  1. 常见 STL 容器的 swap 均为 O(1) 复杂度,而数组为 O(n)

  2. vectorname.clear() 可以删除元素,但不能释放内存,若释放内存可采用 vector<type>().swap(name)

  3. 可重集使用 multisetfind() 可查询元素的迭代器,删除单个元素可采用 name.erase(name.find()),删除所有元素可采用 name.erase()(单个元素迭代器唯一,而相同元素可重)。

  4. memset 整体赋正无穷大 / 负无穷大,分别用 0x3f0xc0,两个无穷大相加不会爆范围。

  5. string 相关:

    • cincout 进行读写;
    • substr(pos,len) 可以提取下标从 pos 开始长为 len 的子串,当只传入 pos 一个参数的时候一直提取到结尾。

技巧杂项

  1. 改变序列顺序对答案无影响,可以先排序。证明排序正确性等价于证明交换任意两项不改变答案。

  2. 计数类问题正难则反,可以用方案总数减去不合法部分。

  3. 最大值最小、最小值最大等问题,常见想法是二分答案,从而将最优解问题转化为判定性问题。

  4. 01 二分答案:操作不改变序列元素大小,询问一系列操作结束后某一位置的值,一个技巧是 01 二分答案,即二分该位置的答案 mid,将大于 mid 的值赋 1,其余赋 0,起到把值域压缩成 {0,1} 的效果。见 P2824AT-AGC006D

  5. CDQ 分治通常能够解决以下形式的问题:

    • 点对相关(例如三维偏序);
    • 优化一维 DP 的转移(例如二元最长上升子序列 fi=1+maxj=1i1fj[ai<aj][bi<bj],见 P2487);
    • 将动态问题转化为静态问题。
posted @   Zwb0106  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示