【SinGuLaRiTy-1006】 ZKW-RegTree ZKW线段树

本文详细介绍了ZKW线段树,包括它的由来、建树过程、单点查询和区间查询。ZKW线段树由张昆玮发明,特点是常数小、速度快。文章通过实例解释了如何利用差分思想存储数据,以及如何进行单点查询和区间查询,特别是区间查询中对节点奇偶性的判断和应用。
摘要由CSDN通过智能技术生成

 Code By WenJian

{作者还在理解开区间的问题,一旦有新的想法将会及时更新}

【关于ZKW线段树】

Zkw线段树是清华大学张昆玮发明非递归线段树的写法。实践证明,这种线段树常数更小,速度更快,写起来也并不复杂。


【建树】

ZKW线段树本质上就是依赖于满二叉树中父节点与子节点的编号关系。


如上图中的一个简单的满二叉树,我们可以发现如下规律:

1>父子节点编号关系: 假设父节点的编号为 n ,那么,它的两个子节点的编号就分别为 n*2(n<<1)、n*2+1(n<<1|1);

2>二叉树层数与底层叶子节点数的关系:假设这个二叉树的层数为 m ,那么,这个二叉树的底层叶子节点数(由于是满二叉树,这也就是所有的叶子节点了)就是2^(m-1),同时,我们还可以知道,所有叶子节点中编号最小的,即在这个满二叉树左下角的叶子节点的编号也为 2^(m-1);

通过以上的两大关系,我们在存储一个数组的初始数据时,就可以直接将初始数据存储在满二叉树的底层。假设数组中有 x 个元素,那么这 x 个元素在这个满二叉树中的编号就是2^(m-1)~2^(m-1)+x-1,访问起来就很方便了。

于是就有了建树代码:(其中n代表的是初始数组中的元素个数,M代表的是最底层的叶子节点个数)

inline void Build()
{
    for(M=1;M<n;M<<=1) ;//由于要构建一个满二叉树,所以我们不能直接让二叉树的叶子节点数等于元素个数,M可能会大于n;本层循环使底层叶子节点数在满足“满二叉树的前提下最小”
    for(int i=M;i<n+M;i++)//由于M也同样是本层最左侧叶子节点的编号,所以直接从这里开始赋值
        Tree[i]=Read();
}
(许多其他的博客总是会在这里自问自答“建完了吗?没有。”,对于这种有点SB的行为,我表示无法理解)

不过确实,到目前为止,建树还未完成,我们还需要从下往上更新其它节点的值。当然,知道了父子节点编号的关系,这个操作就非常好用了。、

inline void upgrade()
{
    for(int i=M-1;i;i--)
    {
        Tree[i]=Tree[i<<1|1]+Tree[i<<1];//维护为区间和
    }
}
当然,你也可以将其维护为最大值,最小值之类的,代码都大同小异:

最大值:

Tree[i]=max(Tree[i<<1|1],Tree[i<<1]);
最小值:
Tree[i]=min(Tree[i<<1|1],Tree[i<<1]);

到目前为止,我们才算是完成了ZKW线段树的建树工作。

<ZKW线段树中的差分思想>

在建ZKW线段树的过程中,可以用的Tree[i]表示父子节点的差值,也同样可以达到存储数据的目的。

void Build(int n)
{ 
    for(M=1;M<=n+1;M<
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值