0.简介

zkw线段树是一种非递归线段树,与普通线段树不同的是,它是棵标准的满二叉树,所以遍历过程可全程使用位运算,常数一般比线段树小得多。

1.结构/建树

前面说了,zkw线段树是满二叉树,可是原数组大小不一定是2^n,所以我们就多开一些废点,硬充,另外,它需要左右各两个点当哨兵,原因看下面的查询原理就知道了。

有人会想,开一些废点是不是空间会更大,相反,一般线段树由于相比之下结构混乱,一般开4倍,而zkw只用开3倍。

这就是zkw:

在zkw线段树中,最下面一排的点就是原数组上的点,因为是满二叉树,所以这些点的深度都一样,设为d(1的深度为0,图中d为4),然后会发现底排上面的点(从1~15)数量刚好为2^d-1,原数组中的元素编号(最底下红色字体)对应的线段树上的点的编号恰好增加了2^d,最低排点数也为2^d(包括废点),那么我们只需要知道这个常数d,就可以确定这棵树的结构了,所以建树很简单,只需要计算最小的、使最低排点数足够的d,实际上,计算2^d后面更方便:

int M; //M 即 2^d
void maketree(int n) {//原数组长度
M = 1; while(M < n+2/*包括哨兵*/) M <<= 1;
}

2.单点修改

我们知道了2^d,就可以直接求出原数组中某个元素在线段树上的点,然后一路向上更新(编号/2下取整即为其父亲的编号):

void addtree(int x,int y) {//y的类型视情况而变
int s = M + x;
tre[s] = y; s >>= 1;
while(s) {
tre[s] = Merge(tre[s<<1],tre[s<<1|1]);//自定义的某种合并操作,可以是相加、相乘、最大值最小值等
s >>= 1;
}
}

3.区间查询

当我们知道要查询的区间[l,r]时,就把区间两端往外的两个点记录为s(M+(l-1)),t(M+(r+1)),那么两点到 lca 路径包围住的点就恰好是要查询的区间,把s和t往父亲方向走,每当s为其父亲的左儿子(s为偶数),那它的兄弟一定在区间里(s ^ 1),每当t为其父亲的右儿子(t为奇数),那它的兄弟也一定在区间里(t ^ 1):

int findtree(int l,int r) {
int s = M + l - 1,t = M + r + 1;
int ans = 0;
while(s || t) {
if((s>>1) ^ (t>>1)) { // s/2 != t/2
if(!(s & 1)) ans = Merge(ans,tre[s ^ 1]);
if(t & 1) ans = Merge(ans,tre[t ^ 1]);
}
else break;
s >>= 1;t >>= 1;
}
return ans;
}

4.永久化懒标记

zkw线段树的修改和查询都是从下往上做,因此懒标记不方便往下放,于是就用永久化的懒标记,记录整个子树的修改(不包括子树的根),以后每次查询都要按照深度依次修改答案。

用懒标记就可以支持不按时间顺序修改的区间修改了,和不用懒标记的区间查询差不多,但是要修改访问到的区间内点的懒标记,并且更新经过的所有节点,也就是说不能中途break了。

以区间最大值为例:

void addtree(int l,int r,int y) { //区间修改
int s = M + l - 1,r = M + r + 1;
while(s || t) {
if(s < M) tre[s] = max(tre[s<<1],tre[s<<1|1]) + lz[s]; //lz[]为懒标记数组
if(t < M) tre[t] = max(tre[t<<1],tre[t<<1|1]) + lz[t]; //路途中的节点修改,但不包括最下面一排,因为它们没有子节点
if((s>>1) ^ (t>>1)) {
if(!(s & 1)) tre[s^1] += y,lz[s^1] += y;
if(t & 1) tre[t^1] += y,lz[t^1] += y;
} //不能break
s >>= 1;t >>= 1;
}
}
void findtree(int l,int r) { //区间查询
int s = M + l - 1,r = M + r + 1;
int ls = 0,rs = 0; //左右两边单独算,因为左边每一个父亲只能照顾左边路径上的子孙,右边同理
while(s || t) {
ls += lz[s];
rs += lz[t];
if((s>>1) ^ (t>>1)) {
if(!(s & 1)) ls = max(ls,tre[s^1]); //这里不能考虑懒标记,因为懒标记省略的修改不包括自己
if(t & 1) rs = max(rs,tre[t^1]); //当然,这只是笔者个人的写法,看官随意
}
s >>= 1;t >>= 1;
}
return max(ls,rs);
}

5.其他

除了这些操作,zkw还可以支持线段树二分,只不过只有到最底层的时候,才能知道具体的位置。

记录每个点的具体区间范围、长度,甚至可以将就打递归版。

此外,zkw线段树结构的性质还可以运用到其他题目上。

zkw线段树——简单易懂好写好调的线段树的更多相关文章

  1. 迅为4412开发板Linux设备树的镜像烧写和源码简单优化教程

    1 烧写:   烧写和4412默认镜像的烧写类似,使用fastboot. 先更新uboot,用4412默认uboot更新支持设备树的uboot 用支持设备树的uboot烧写. 进入支持设备树的uboo ...

  2. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  3. UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

    原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$ ...

  4. 【repost】让你一句话理解闭包(简单易懂)

    接触javascript很久了,每次理解闭包都似是而非,最近在找Web前端的工作,所以需要把基础夯实一下. 本文是参照了joy_lee的博客 闭包 在她这篇博客的基础上以批注的形式力争把我的理解阐述出 ...

  5. UE4中的AI行为树简单介绍

    UE4引擎中可以实现简单AI的方式有很多,行为树是其中比较常用也很实用的AI控制方式,在官网的学习文档中也有最简单的目标跟踪AI操作教程,笔者在这里只作简单介绍. AIController->和 ...

  6. 【转】JS回调函数--简单易懂有实例

    JS回调函数--简单易懂有实例 初学js的时候,被回调函数搞得很晕,现在回过头来总结一下什么是回调函数. 我们先来看看回调的英文定义:A callback is a function that is ...

  7. java生成RSA公私钥字符串,简单易懂

    java生成RSA公私钥字符串,简单易懂   解决方法: 1.下载bcprov-jdk16-140.jar包,参考:http://www.yayihouse.com/yayishuwu/chapter ...

  8. HashSet的实现原理,简单易懂

    HashSet的实现原理,简单易懂   答: HashSet实际上是一个HashMap实例,都是一个存放链表的数组.它不保证存储元素的迭代顺序:此类允许使用null元素.HashSet中不允许有重复元 ...

  9. 用最简单的代码写出banner图轮播效果

    以下视频是由[赵一鸣随笔]博客提供的“用最简单的代码写出banner图轮播效果”. 查看全屏高清视频,请点击链接:http://www.zymseo.com/58.html

随机推荐

  1. Winforms选择文件夹、文件

    更新记录: 2022年5月28日 初始记录 选择文件夹 if (this.folderBrowserDialog1.ShowDialog() == DialogResult.OK) { //获得用户选 ...

  2. vue大型电商项目尚品汇(后台篇)day03

    今天把平台属性的管理基本完成了,后台管理做到现在基本也开始熟悉,确实就是对ElementUI的一个熟练程度. 一.平台属性管理 1.动态展示数据 先把接口弄好,应该在第三级标题选择后进行发请求 静态页 ...

  3. 实现领域驱动设计 - 使用ABP框架 - 通用准则

    在进入细节之前,让我们看看一些总体的 DDD 原则 数据库提供者 / ORM 无关性 领域和应用程序层应该与 ORM / 数据库提供程序 无关.它们应该只依赖于 Repository 接口,而 Rep ...

  4. Python教程——常用的os操作详情

    Python自动的os库是和操作系统交互的库,常用的操作包括文件/目录操作,路径操作,环境变量操作和执行系统命令等. 文件/目录操作 获取当前目录(pwd): os.getcwd() 切换目录(cd) ...

  5. 测试人员假装自己会“devops”

    目录 什么是DevOps DevOps岗位职责 DevOps的要求 如何增强DevOps能力 准备 服务器端安装jenkins Jenkins jenkins + maven 构建 jenkins下m ...

  6. HashMap存储自定义类型键值和LinkedHashMap集合

    HashMap存储自定义类型键值 1.当给HashMap中存放自定义对象时,如果自定义对象是键存在,保证键唯一,必须复写对象的hashCode和equals方法. 2.如果要保证map中存放的key和 ...

  7. Aspose最新版文档格式转换使用破解

    Aspose简介 Aspose.Total是Aspose公司旗下全套文件格式处理解决方案,提供最完整.最高效的文档处理解决方案集,无需任何其他软件安装和依赖.主要提供.net.java.C++d三个开 ...

  8. 在eclipse配置javafx

    JAVA学习中,遇到了这个问题,解决方法记录一下(我用的jdk11) 最新几版的eclipse中没有javafx,需要自己进行手动配置,先下载一下javafx的包,解压,找到lib文件夹(主要用的是这 ...

  9. 建立二叉树的二叉链表存储结构(严6.70)--------西工大noj

    #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct TreeNode ...

  10. go交叉编译,部署到linux上出现cannot execute binray file的解决方案

    写在前面: 了解过常见的项目部署方式后,打算先从最简单的方式开始.没想到踩了大坑.先说下整个部署的过程.    博主是在window上生成linux上的可执行文件.. 具体过程: 1.首先按照网上说的 ...