维护区间幺半群信息的科技
有一个长度为 \(n\) 的序列 \(a\),定义一个二元运算 \(x\circ y\),它满足结合律。\(q\) 次询问,每次求 \(a_l\circ a_{l+1}...\circ a_r\)。
线段树是解决这种问题的数据结构之一。猫树可以在 \(\Theta(n\log n)-\Theta(1)\) 内解决。sqrt-tree 可以在 \(\Theta(n\log\log n)-\Theta(\log\log n)\) 内解决,但利用二进制的特性可以把查询优化到 \(\Theta(1)\)(使用builtin
系列函数)。先讲一下易于理解的 sqrt-tree。
在P3793 由乃救爷爷中用到了分块然后对每一块维护前后缀信息,再维护块间信息的方法。这种方法在非随机数据下十分优秀。但我们是理论计算机科学家,我们非要让它理论复杂度严格。
于是就有一位老哥跳出来说我们可以把这个东西递归下去,即sqrt-tree。也就是对于每个小块都用这种分块维护。然后这样就形成了一颗树,树高是 \(\Theta(\log\log n)\)。
但是这个做法就有个很哈的地方,就是块间的信息其实是一个子问题,但是它却是用的暴力。
注意到可以用猫树维护块间信息,并且把块长调到 \(log\)。这样每递归一层子问题都会变成原来的 \(\log\) 规模,树高只有 \(\Theta(\log^*n)\)。因为我们一直保证了每一层的时间是 \(\Theta(n)\) 的,所以这个复杂度就是 \(n\) 乘上树高。
我们把单纯的猫树这一数据结构叫做第 \(0\) 层数据结构,上面说的数据结构叫做第 \(1\) 层数据结构。下面我们来搞一个第 \(m\) 层数据结构,用 \(T_m(n)\) 表示 \(m\) 层数据结构在长度为 \(n\) 的序列上的树高。比如 \(T_0(n)=\log n,T_1(n)=\log^*n\)
由于到了第 \(m\) 层,所以这个复杂度也不完全是树高乘 \(n\)。因为块间查询最坏要依次递归第 \(m-1...0\) 层数据结构,所以复杂度应该是 \(\Theta(n*m)\) 的。
第 \(m\) 层以 \(T_{m-1}(n)\) 为块长,块中间的信息用 \(m-1\) 层数据结构维护。这样保证了对块中间信息的处理是 \(\Theta(n)\) 的。得到树高为 \(T_m(n)=T_{m}(T_{m-1}(n))+1\)。
找到一个 \(T_m(n)=1\) 使得树高为 \(1\) 即可,有结论 \(m=\alpha(n)\),这是一个总的复杂度为 \(\Theta((n+q)\alpha(n))\) 的算法,已经是此类问题的下界了。
其实用处并不大废话理论计算机科学里的东西能有啥用,就算真的要卡时间,sqrt-tree 表现一般也比这个优秀。而且比较常见的适合用这种方法维护的运算大概只有 \(\gcd\) 和矩乘?