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. Aeraki Mesh正式成为CNCF沙箱项目,腾讯云携伙伴加速服务网格成熟商用

    6月,由腾讯云主导,联合百度.灵雀云.腾讯音乐.滴滴.政采云等多家合作伙伴发起的服务网格开源项目 Aeraki Mesh 通过了全球顶级开源基金会云原生计算基金会(CNCF)技术监督委员会评定,正式成 ...

  2. vue 使用npm install安装依赖失败 【问题分析与解决】

    1 进入项目根目录,先通过 npm install 命令安装项目所需依赖,再通过 vue ui 命令打开 Vue Cli 提供的图形化界面,选择项目所在文件夹将项目导入. 出现问题 npm insta ...

  3. VMware 安装 Anolis OS

    安装时参考的以下文章 VMware Workstation Pro 虚拟机安装 VMware Workstation Pro 安装 龙蜥操作系统(Anolis OS) Anolis OS 8 安装指南 ...

  4. 3.shell脚本循环试题

    shell脚本循环试题 1.计算从1到100所有整数的和 #!/bin/bash a=0 for i in {1..100} #1到100 #每次循环变量i的值也为循环次数 do a=$[ $a + ...

  5. 20天等待,申请终于通过,安装和体验IntelliJ IDEA新UI预览版

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于IDEA的预览版 IDEA会启用新的UI,这事情之 ...

  6. MySQL 锁常见知识点&面试题总结

    节选自 <MySQL 常见知识点&面试题总结> 表级锁和行级锁了解吗?有什么区别? MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发 ...

  7. 提名 Apache ShardingSphere Committer,说说方法

    文章首发在公众号(龙台的技术笔记),之后同步到博客园和个人网站:xiaomage.info 就在前几天,收到了 Apache ShardingSphere Vote 我成为 Committer 的邮件 ...

  8. 【问题解决】Axios调用文件下载获取不到文件名

    问题描述 自己开发了一个后端下载文件的接口,然后通过浏览器直接访问这个接口,浏览器能正确得到文件名并下载: 而使用Axios时发现获取不到,经过打印响应体发现响应头里没有文件名 而Java后端代码里是 ...

  9. 华为云Stack南向开放框架,帮助生态伙伴高效入云

    摘要:CloudBonder的生态社区通过一系列生态项目,解决提交叉组合.架构分层不清晰.运维界面不清晰等问题,简化对接流程,降低生态伙伴对接成本,缩短对接时间. 本文分享自华为云社区<[华为云 ...

  10. ApiDay002_01 正则表达式

    正则表达式 用于检测.测试字符串规则的表达式. 经常用于检测字符串是否符合特定的规则,在网站上经常用于检测用户输入数据是否符合规范: 检测 用户名 是否为 8~10 数字 英文(大小写) 检测 电话号 ...