do_while_true

一言(ヒトコト)

「学习笔记」区间半群信息查询

qwaszx 博客 的抄写。

半群信息可以简单理解为有结合律的信息。比方说 \(\gcd,\min,\max,+,\times\) 之类的。

这一部分的东西在 OI 中没什么用,就当学个乐吧()

猫树 和 Sqrt Tree 可能有些用。

朴素做法

\(\Theta(n^2)\) 预处理所有区间的答案,然后可以 \(\Theta(1)\) 询问。

线段树 树状数组 ST 表

只要是半群信息都可以用线段树维护。而树状数组则要求信息可以差分,ST 表要求是幂等半群信息(\(x\circ x=x\))。

猫树

考虑线段树上分治,对于每个端点代表的区间 \([l,r]\) 及其中点 \(m\),预处理区间内所有以 \(m\) 为结尾的后缀信息和以 \(m+1\) 为起点的前缀信息。那么每次查询 \([l,r]\) 的区间信息的时候,只需要定位到 \([l,r]\) 第一次分裂的节点,也就是 \(l,r\) 节点的 LCA,那么就可以 \(\Theta(1)\) 查询了。

那就可以把序列补成 \(2\) 的次幂,这样 LCA 就相当于定位到 \(l,r\) 的二进制下的 lcp,可以 \(\Theta(1)\) 求得。

把序列补成 \(2\) 的次幂还要考虑空的位置要为幺元,不存在的幺元的话,直接钦点一个幺元,合并的时候特判一下就好了。(被 qyc 教育了)

预处理时间复杂度 \(\Theta(n\log n)\),单次询问 \(\Theta(1)\)

Sqrt Tree

把序列按照 \(\sqrt n\) 分块,对于整块之间的答案,可以用上文的朴素做法 \(\Theta(n)\) 预处理,散块也可以 \(\Theta(n)\) 预处理块内前缀信息和后缀信息。这样跨块的询问都能 \(\Theta(1)\) 询问,块之内的递归下去处理即可。

递归层数是 \(T(n)=T(\sqrt n)+\Theta(1)=\Theta(\log \log n)\) (每次递归指数 \(/2\)),询问的话和上节一样,定位需要用到 LCA,合并是预处理的三个信息进行合并。

时间复杂度 \(\Theta(n\log \log n)-\Theta(1)\)

Sqrt Tree 还能 \(\mathcal{O}(\sqrt n)\) 单点修改,这个先咕着,也有可能单独再写个文章专门写 Sqrt Tree。

在 Sqrt Tree 中块间的预处理中,使用朴素做法并进行复杂度平衡,得到了 Sqrt Tree。类似地,块间可以使用猫树维护,为了复杂度平衡,采用 \(\log n\) 为块长。这样每次递归下去问题规模变为 \(\Theta(\log n)\),树高为 \(\Theta(\log^{\ast}n)\),时间复杂度就是 \(\Theta(n\log^{\ast}n)-\Theta(1)\)

Extend

没有更好的理解,只能进行抄写。

在上节中,用猫树维护 Sqrt Tree 得到了一个树高为 \(\Theta(\log^{\ast}n)\) 的数据结构。那么可以继续对上述过程进行迭代:假设上一层数据结构深度为 \(T(m-1,n)\),那么以 \(T(m-1,n)\) 分块,块间用上一层数据结构进行维护,块内递归处理。这一层数据结构的深度就是不断令 \(n\gets T(m-1,n)\) 直到 \(n=\Theta(1)\) 的次数。

为了找到最优的复杂度,考虑找到最小的 \(m\) 使得 \(T(m,n)=\Theta(1)\),通过查表(不知道 qwaszx 咋查的)发现 \(m\) 就是反阿克曼函数 \(\alpha(n)\)

考虑每次询问以 \(\Theta(1)\) 的复杂度进入上一层数据结构,则询问复杂度为 \(\Theta(\alpha(n))\);预处理的时候,在第 \(m\) 层的数据结构需要花费 \(\mathcal{O}(nT(m,n))\) 的时空复杂度进入到规模(也就是分块的块数)为 \(\mathcal{O}(nT(m,n)/T(m-1,n))\) 的下一层数据结构,因此预处理的时空复杂度为 \(\mathcal{O}(n\alpha (n))\)(这里 qwaszx 说是查表查出来的),总时间复杂度为 \(\mathcal{O}((n+q)\alpha(n))\)

qwaszx 说这个是下界,但我并不知道原论文是啥()

posted @ 2022-02-14 21:28  do_while_true  阅读(763)  评论(0编辑  收藏  举报