【学习笔记】猫树

猫树是解决无修改区间或树上询问的高效算法, 对于线段树不好写 \(pushup\) 的问题, 可以考虑采用猫树, 构造 \(n\log{n}\) , 询问 \(\mathcal{O}(1)\) , 空间 \(n\log{n}\) .


对于一个询问 \([al,ar]\) , 如果 \(al=ar\) , 就可以直接得到答案, 否则我们考虑在线段树上定位, 它会在几个 \(mid\) 处分开.

考虑第一次被分开的位置, 假设为 \(p\) , 那么原来区间的就被分为 \([al,mid]\)\([mid+1,ar]\) .

对于每个 \(mid\) 预处理它向前的后缀缀, 即 \([i,mid]\) 答案, 对于它向后的前缀, 即 \([mid+1,j]\) 的答案.

如果我们知道了 \(p\) 所在位置, 我们可以直接合并 \([al,mid]\)\([mid+1,ar]\) 的答案即可.

不难发现预处理的复杂度是 \(\mathcal{O}(n\log{n})\) , 总共 \(\log{n}\) 层, 每层每个数恰好被计算一次.

怎么快速知道 \(p\) 这个位置, 不难发现 \(p\) 就是 \([al,al]\)\([ar,ar]\)\(lca\) , 所以我们可以考虑 \(ST\) 表预处理, 然后直接查询.

如果整棵线段树满足堆试储存 (即左子树编号为 \(2i\) , 右子树编号为 \(2i+1\) ) , 就有很好的性质.

对于任意深度相同的点, 它们的 \(lca\) 是它们二进制下的 \(lcp\) .

如果我们把整棵树建成一个满二叉树 \([1,2^k]\) , 那么对于任意一个区间 \([i,i]\) 都是满足他们的深度是最深且在同一层的, 这很显然.

所以对于两个数 \(x,y\) 的二进制下的 \(lcp\)\(x>>\log_2{x\oplus y}\) , 这也很显然, 丢掉第一个不相同的位后面的所有位.

这样就实现了 \(\mathcal{O}(1)\) 询问.

posted @ 2022-09-26 20:46  Lonely923  阅读(65)  评论(1编辑  收藏  举报