题面转化 tricks

从复杂度入手

以下展示了一些经典的算法的复杂度。当然还有很多遗漏的,接下来慢慢补。

数据规模(以1000ms时限为例) 典型的对应算法 复杂度
\(1\le n\le 10\) 全排列 \(O(n!)\)
\(1\le n\le C\times 20\) 搜索;状态压缩 \(O(2^n)\)(注意常数对复杂度带来的毁灭性影响)
\(1\le n\le 100\) Floyd;矩阵乘法 \(O(n^3)\)
\(1\le n\le 10^3\) 很多,写不下 \(O(n^2)\)
\(1\le n\le 5\times 10^5\) 分块;莫队;根号分治;均摊(如BSGS) \(O(n\sqrt{n})\)
\(1\le n\le C\times 10^6\) 线段树(常数很大);树状数组;分治;倍增预处理; \(n\)\(O(\log n\)) 操作 \(O(n\log n)\)
\(1\le n\le C\times 10^6/10^7\) 欧拉筛;\(n\)\(O(1)\) 操作 \(O(n)/O(n\log\log n)\)
\(1\le n\le 10^{12}\) 质因数分解;整除分块 \(O(\sqrt{n})\)
\(1\le n\le 10^{18}\) 或者更大(高精度) 快速幂;线段树、树状数组、倍增lca等的修改、查询操作;log函数;set/map等stl;vector(均摊);优化并查集 \(O(\log n)/O(\log\log n)\)
\(1\le n\le 10^{18}\) 或者更大(高精度) st表查询;预处理如前缀和和差分;单调栈、单调队列;双优化并查集 \(O(1)/O(\alpha(n))\)(反阿克曼函数)

“抽象”操作转化

如果你遇到了一个陌生和古怪的操作,然后又让你快速计算某种结果或是计数,可以考虑找到一个在操作中不变的量,或是与操作等价的弱化版问题,从这个量出发思考问题。

这实际上就是区间数颜色等问题的背后逻辑。在此列举出一些冷门的转化。

\(a\in\{1,2,3\}\) 消除相邻两个不同数 变成 没出现的另一个

区间异或和不变,而且异或和代表了最终得到的字母。

神秘数

一个可重集合的 神秘数 定义为将该集合中若干个数字相加无法得到的最小正整数。

找到第一个 \(k\) 满足 \(\sum\limits_{i=1}^k a_i < a_{i+1}-1\),那么神秘数就是 \((\sum\limits_{i=1}^k a_i )+1\)。证明使用数学归纳法易证。

建图

查询区间异或和

众所周知,同一个数异或两次会抵消。定义 \(f(a,b)\) 表示查询区间 \([a,b]\) 的异或和。那么有 \(f(a_1,a_2)\bigoplus f(a_2,a_3)\bigoplus ...\bigoplus f(a_{n-1},a_n)=f(a_1,a_n)\) 。注意这里规定 \(f(a,b)=f(b,a)\) ,不限制 \(a\le b\)

\(f(a,b)\) 当成从 \(a\)\(b\) 的边,那么这有点像图论中的连通性问题。

运用:给定长度为 \(n\)\(0/1\) 序列,你可以花费 \(f_{i,j}\) 代价查询区间 \([i,j]\) 的异或和,求得知序列所有位置答案的最小代价。

解法:容易发现,这是一个连通性问题,你的目的是使所有 \(n+1\) 个数字间的“缝隙”联通。最小生成树可以完成这个问题。

最小值DP转为最短路

如果要求某个些状态的最小值,由不方便转移,可以考虑记忆化搜索把转化为节点,把转移方式转化为有向边,跑最短路算法。当然你不必把所有边都建出来,只有要用的时候算一下就行。

容斥原理

正难则反

例子

本题如果直接统计纯黑色三元组和纯白色三元组会很复杂。考虑统计杂色三元组。一个杂色三元组的边构成是 黑黑白 或者 黑白白。考虑对于每个点统计与之相连的 \(黑边数\times白边数\) 加和,得到了答案的两倍(因为一个杂色环一定具有2个杂色角)。

转化二分

如果问题中值域只有 \(0\)\(1\) 会更好做,并且求的是序列中某个位置/某种操作下得到的单个数,且操作与实际值无关只与大小有关,那么可以二分答案,把 \(\le mid\) 的标为 \(0\)\(\ge mid\) 的标为 \(1\)

中位数最值

如果要求某些条件下中位数的最值,可以二分答案。把 \(< mid\) 的当作 \(-1\)\(>mid\) 的当作 \(1\),这样只需求出是否有合法的和 \(\ge 0\) 的即可。

其他经典的tricks

可差分量的区间查询/修改(不能既查又修)

直接差分,转化为两点的问题,使用 并查集/图论/最小生成树 之类算法解决。

区间内不同颜色

每个位置维护一个 \(pre_i\) 表示上一次相同的元素。如果 \(pre_i<l\) 说明区间 \([l,r]\) 中没有与 \(a_i\) 重复的元素。其他的运用同理。这是非常常见的trick,一般用线段树等数据结构维护。

mex

一个集合 \(S\) 的mex是指最小的不属于 \(S\)非负整数

mex具备一个有趣的性质:如果集合不包含0,那么它的mex就是0

只能在mex(S)没被删除时删除集合S

如果要删除0,那么0一定是最后删除,因为删除了0之后,别的要删的时候就没有mex了,也就删不了了。

区间查询mex

我以为很简单,但是貌似有难度,要用带修莫队+值域分块:CF940F

破环为链

常见于区间DP、线性DP。将环复制两份,当作链处理即可。注意不能允许把同一个元素计算两次的情况。

走路遍历一棵树

每次走一条边,遍历整棵树的需要的次数是 \((点数-1)\times 2-到起点的最远点距离\)。这是因为走到最远点后就不用回到起点了,这些边只被走了一遍。

posted @ 2025-02-20 21:20  Luke_li  阅读(6)  评论(0)    收藏  举报