计算理论,复杂度平衡
前置
P:多项式复杂度能被图灵机判定的语言。即 \(O(n^k)\),其中 \(n\) 为输入长度。
NP:“其解的正确性可以在多项式时间内被验证。”
规约:若你能用 B 的解法解决 A,则 B 至少和 A 一样难,那么 A 可以规约到 B。
完全性:
若一个问题比所有的 NP 问题都难,那么它是 NP-Hard。
若一个 NP-Hard 同时也是 NP 的,那么它是 NP-complete。例如 3-SAT,团判定(独立集)问题。
我们将 3-SAT 问题是 NPC 视为公理。
构造性规约:
尝试用 B 解决 A,通常需要进行构造。
证明一个 NP 问题是完全的那么说明其比某个 NPC 问题更难即可。
例:团判定问题与 3-SAT:
对于一个 3-SAT 方程组,每个式子创建 7 个点代表所有赋值方案,对于每两个不矛盾的赋值连一条边,那么 SAT 成立当且仅当存在大小为 \(n\) 的团。
例:哈密顿路径与 3-SAT:
对于每个式子,创建一个点,记作 \(C_i\);对于每个变量,创建一串点:\(S,A_1,B_1,A_2,B_2,\dots,A_n,B_n,T\)。
从第 \(i\) 个变量的 \(S,T\) 向第 \(i+1\) 个变量的 \(S,T\) 连边。用双向边把每个变量的一串点连成链;
若第 \(i\) 个变量为真能让第 \(j\) 个式子成立,则从第 \(i\) 个变量的 \(A_j\) 向 \(C_j\) 连边,\(C_j\) 向 \(B_j\) 连边;
若第 \(i\) 个变量为假能让第 \(j\) 个式子成立,则从第 \(i\) 个变量的 \(B_j\) 向 \(C_j\) 连边,\(C_j\) 向 \(A_j\) 连边。
跑哈密顿路径,考察变量 \(i\) 对应的链是从 \(S\) 到 \(T\) 还是从 \(T\) 到 \(S\),这对应第 \(i\) 个变量取值真假。
在 OI 中的应用
网络流:一旦加入一些限制问题将变成 NP-Hard。
-> 带负环的费用流
-> 要求一些边要么不流,要么满流。
我们要避免魔改模型导致变成不可做的模型,即可以知道我们的转化是否是对的。
矩阵乘法:证明一些问题强于矩阵乘法可以得到问题复杂度下界。通过构造性规约。
以下问题都强于 \(\sqrt n\times \sqrt n\) 的矩阵乘法:
-> 树上数颜色。
-> 区间逆序对/出现次数平方和。
以下问题不比矩阵乘法弱:
-> 01 矩阵乘法。
-> (min,+) 矩乘。
(min,+) 卷积:
如果两个数组均凸可以直接民可夫斯基和。
如果一个数组是凸的可以做到 \(O(n\log n)\)。(决策单调性)
一般情况只有 \(O(n^2)\)。包括 (min,max) 卷积。
其他:
-> 3-Sum 问题。\(n^2\);
-> 传递闭包 \(nm\);
-> 全源最短路 \(n^3\);
复杂度分治
分块:分块法大体分为两种。
-> 多项式分块,比如根号。
-> \(\log\) 分块,一块大小 \(\log n\),如果是 \(01\) 序列,那么所有块只有 \(O(2^{\log n})=O(n)\) 类,\(O(1)\) 计算一块。
k 叉线段树:
单点加 \(O(\log_kn)=O(\frac{\log n}{\log k})\);区间加:\(O(\frac{\log n}{\log k}k)\)。
若加的次数是和的 \(k\) 倍,那么取 \(k\) 叉数最优。
例如 \(O(n)\) 次求和,\(O(n\sqrt n)\) 次加法取 \(\sqrt n\) 叉树也就是分块。
P9969 [THUPC 2024 初赛] 分治乘法
考虑一个简单的想法就是每次分一半然后合并两边,代价是 \(O(n\log n)\) 级别的,操作次数 \(O(n)\)。
问题是没用到操作 \(3\),考虑将表示集合的 01 序列分块,设块长为 \(B\)。
那么现在只有 \(2^B\) 种块,相同的块可以通过平移实现,然后合并起来。
有两种平移的方式,一种是把一个块搞好然后一个个块平移出来。这个是 \(O(n+2^B\log B)\)
另一种是所有块搞好第一个元素再一个一个元素平移出来,这个是 \(O(n+\frac{n}{B}\log \frac{n}{B})=O(n+\frac{n}{B}\log n)\)。
然后形成了若干个集合,第一种有 \(O(\frac{n}{B})\) 个集合,第二种有 \(O(B2^B)\) 个集合。
那么剩下的复杂度分别是 \(O(n\log \frac{n}{B})=O(n\log n)\),第二种是 \(O(n\log B2^B)=O(nB)\)。
发现第一种完全没有优化,考虑第二种取 \(B=\sqrt{\log n}\),复杂度为 \(O(n\sqrt{\log n})\)。
这里优化的原理是什么呢?我们通过平移的操作使得合并的深度减少。
牛客练习赛 73 F 回音
给定序列和区间序列,询问时取出一个区间序列里的一个区间,取出这些所有区间的数(可能取出多次),问第 \(k\) 小。\(n\le 10^5\)。
构式题目考虑分块。
第 \(k\) 小考虑值域分块,确定每个答案在哪个块内,我们需要算每个元素在区间序列的区间内出现次数。
那么考虑对于每个块都单独做一遍。考虑将区间序列的区间差分一下,然后对区间序列的下标做扫描线。
设在块内的元素为 1,其他的为 0,那么我们只需要支持区间和即可。
然后考虑查询一个值的出现次数,即求跨越某位置的区间个数,这个也是扫描线区间加单点查。
发现查询是 \(O(n\sqrt n)\),修改是 \(O(n)\) 的,所以根号平衡一下,复杂度就是 \(O(n\sqrt n)\)。
第二个部分还有一种扫描线的方式是对序列的下标进行扫描线。也是根号平衡。
P3350 [ZJOI2016] 旅行者
注意到 \(n\times m\le 20000\),那么 \(\min (n,m)\le \sqrt {20000}\)。
考虑对长边分治,沿着短边将图切成两半。每次算经过当前分治线的路径。
Qoj # 7895. Graph Partitioning 2
联通块数 * 块大小 = n,考虑根号分治。所以设计两种 dp,事实上只是两种不同的状态表示方式。
以子树为子任务划分, \(dp_{u,j}\) 表示 \(u\) 子树内顶端是大小为 \(j\) 的联通块数的方案数;
或者表示 \(u\) 子树内划分了 \(j\) 个大小为 \(k\) 连通块的方案数。转移是树形背包,复杂度是 \(O(n\sqrt n)\)。
进一步的,事实上两种状态表示是等价的。所以无论如何状态数不超过 \(O(n\sqrt n)\)。
所以用第一种 dp 用 unordered_map 存即可。
Uoj #246. 【UER #7】套路
最小值 * 区间长度 \(\le n\),即鸽巢原理,考虑根号分治。
对于区间长度小的,可以有一个递推,\(mn_{l,r}=\min(mn_{l+1,r},mn_{l,r-1},|a_l-a_r|)\)。
对于最小值小的,考虑直接枚举最小值是多少,从小到大递推最小值,
维护 \(l_i\) 表示右端点为 \(i\),左端点最左多少。假设现在最小值为 \(k\),直接跟 \(pre_{a_i-k},pre_{a_i+k}\) 取 \(\max\) 即可。
这个维护很典。
CF1768F Wonderful Jump
观察到 \(\min\times len\le n\),否则转移无用,然后后面就很典了。
首先对于距离 \(\le \sqrt n\) 的都转移过来。
观察到转移的一段区间中间不会存在比两端小的数(调整可得)。然后只需要从 \(1\sim \sqrt n\) 最后位置转移。
Qoj # 4812. Counting Sequence
观察到 \(\max \times len\le n\),后续需要一个 dp,待补。

浙公网安备 33010602011771号