zkw 线段树瞎写
感觉最近太颓了,要学点亲民的东西,就整了下这个
其实就是非递归的线段树,然后稍微高明一点+卡常一点
可能会有错误,如果发现请往死里喷我
先说树的结构
众所周知线段树递归的原因一方面是在于难以确定点的编号,需要每一次重新从根开始搜索然后得到对应点的编号
因此我们要非递归,就要采用一种更为方便处理的编号手段
考虑一般都是用二进制,而这么处理的前置条件就是堆式建树
于是把序列大小先扩到 $2^k$
另外这里扩到 $2^k$ 之前要先把 $n$ 加一,具体原因后面会说
日报里直接多扩了一倍我是没想到的
那么剩下的点的编号就有很多优美的性质了,一会儿看操作讲
首先考虑建树
假设当前树的大小是 $2^{k+1}-1$
那么发现最后一层的 $2^k$ 个结点刚好就是扩充后的原序列
编号还是连续的,真好
然后考虑 $[1, 2^{k}-1]$ 这些点
显然可以迭代 pushup,就是从 $2^k-1$ 向前循环,然后 pushup 就行
显然这样顺序也是对的
然后考虑单点修改
发现一个位置的编号是确定的
发现一个点的父亲结点的编号一定是 $\lfloor{x\over 2}\rfloor$
于是直接确定修改点的位置,然后把所有祖先 pushup 就行
常数小
考虑单点查询
没标记的情况下甚至是 $O(1)$ 的
考虑区间查询
其实这部分的做法挺高妙的((
考虑整两个指针,分别指向 $l - 1,r + 1$ 对应的结点(所以序列长度要加 1)
然后这两个指针不断往上跳直到父亲相同
在此之前,我们发现只要左指针的右儿子不是它跳上来的方向,就是我们要的结点
右指针同理
这个画图就能理解
而且这种自底向上的可能可能不访问所有结点
常数又变小了((
区间修改
为了减小常数,考虑使用标记永久化(不然 zkw 线段树就失去意义了?)
显然可以用类似区间查询的方法,而且发现我们所有访问到的结点 pushup 即可
发现最后终止状态时,可能还要往上遍历一些点然后 pushup
这里显然直接跳就行
同理区间查询也得跳到根回收标记
空间卡常
这部分多半是口胡的,但感觉挺对的((
上面说了 zkw 线段树不卡常就失去意义了(???)
因此我们继续卡常
我们发现一个性质:t[x] = t[x << 1] + t[x << 1 | 1]
然后我们有时会打 tag ,又要新的空间
这不好,我们好像没有充分利用空间
发现一个性质,如果我们是 pushdown 式的打标记,那么一个 有标记 的结点 pushdown 之后,其儿子的值之和一定变了
因此若 t[x] != t[x << 1] + t[x << 1 | 1]
就是没有 pushdown
所以标记可以直接打在结点上((
欢迎各位大佬来卡掉这个东西,卡掉就能出成一道构造 / 提答 / 结论题了
简单总结一下,上面也说过,这东西除了卡常就没什么用,还不见得有原版线段树好写
实战中一般不会有人特意放 zkw 过然后卡递归线段树
感觉实用性甚至不如什么猫树
不过思想还可以,值得学学