Loading

四毛子算法

(日常口胡)

参考资料:luogu日报 LA 模板题解

一个能把 LCA,RMQ 和 LA 问题做到线性的奇怪技巧。
大概就是分块,块的大小为 \(O(\log n)\),然后整块用原算法,块内可以直接枚举所有可能情况(反正块的大小只有对数级)。

前置知识:ST表做 RMQ,欧拉环游序 LCA 转 RMQ,笛卡尔树,长剖做 LA。

1. LCA

首先我们按照众所周知的方法把问题转成 RMQ。
然后你发现因为跑的是欧拉序,相邻的两个数的差一定是 \(1\)\(-1\)
对于这样一个数列做 RMQ 又叫做 \(\pm1\) RMQ 或 约束 RMQ。
然后我们就可以四毛子了!

令块长为 \(\dfrac{\log n}{2}\),那么一共就分了 \(\dfrac{2n}{\log n}\) 块。
整块直接 ST 表就完事了,时间复杂度 \(O\left(\dfrac{2n}{\log n}\log\left(\dfrac{2n}{\log n}\right)\right)=O\left(\dfrac{2n}{\log n}(\log n+1-\log\log n)\right)=O(n)\)
你会发现块长的 \(\log\) 直接把原来 ST 表预处理的 \(\log\) 给干掉了。
然后我们再考虑块内。
注意到相邻的两个数的差一定是 \(1\)\(-1\),我们作出差分序列,然后钦定 \(0\) 为第一个数还原出一个序列。容易发现这样并没有改变数的大小关系。
因为块长 \(\dfrac{\log n}{2}\) 实在是太小了,这样操作之后不同的序列一共只有 \(O(2^{(\log n)/2})=O(\sqrt{n})\) 种。
于是我们直接暴力枚举,并且对于每一种序列暴力求出区间最大值并且存起来(没必要用 ST 表了)。因此块内时间复杂度为 \(O\left(\sqrt{n}\left(\dfrac{\log n}{2}\right)^2\right)=O(n)\)
\(O(n)-O(1)\),四毛子算法大成功。

2. RMQ

我们已经有了快速的 LCA 做法,于是我们考虑将 RMQ 转到 LCA 上来做。
操作是以数组下标和对应的值作为键值建一棵笛卡尔树,容易发现 \([l,r]\) 的 RMQ 就变成了 \(l,r\) 在笛卡尔树上的 LCA。
\(O(n)-O(1)\) 完事。

3. LA

LA 问题处理起来就比较复杂了。但是和上面处理 LCA 的思路还是相似的。
首先大家知道长链剖分能做到 \(O(n\log n)-O(1)\),瓶颈在于树上倍增求 \(2^k\) 级祖先。
我们对原来的算法做一些神奇的优化。我们可以预处理出每一个非叶子结点到子树中任意一个叶子结点的距离(这个预处理显然是线性的),然后我们只需要对所有的叶子结点做倍增就行了。
但我们发现叶子结点的个数最坏还是 \(O(n)\) 的。仿照之前的做法,我们考虑将一些叶子结点进行单独处理,只留下 \(O\left(\dfrac{n}{\log n}\right)\) 个结点,这样预处理就变成线性的了。

方法是这样的:将子树大小不超过 \(\dfrac{\log n}{4}\) 的结点都删掉,容易发现删去之后树上只有 \(O\left(\dfrac{n}{\log n}\right)\) 个叶子结点,此时运用上面的操作,时间复杂度是 \(O(n)\) 的。
另外已经删去的结点构成了由一些大小不超过 \(\dfrac{\log n}{4}\) 的森林。那我们只需要枚举所有这样大小的有根树并对每种有根树进行预处理即可。注意到这样树的种类数(实际上就是对应的 Catalan 数)大概是 \(O(4^{(\log n)/4})=O(\sqrt{n})\),最终对这些树进行处理的复杂度就是 \(O\left(\sqrt{n}\left(\dfrac{\log n}{4}\right)^2\right)=O(n)\)

可以看出处理手法和 LCA 没什么大的区别,就是实现起来会复杂亿点点。

posted @ 2022-07-30 16:18  pjykk  阅读(645)  评论(0编辑  收藏  举报