zkw线段树——简单易懂好写好调的线段树
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线段树——简单易懂好写好调的线段树的更多相关文章
- 迅为4412开发板Linux设备树的镜像烧写和源码简单优化教程
1 烧写: 烧写和4412默认镜像的烧写类似,使用fastboot. 先更新uboot,用4412默认uboot更新支持设备树的uboot 用支持设备树的uboot烧写. 进入支持设备树的uboo ...
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并
原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$ ...
- 【repost】让你一句话理解闭包(简单易懂)
接触javascript很久了,每次理解闭包都似是而非,最近在找Web前端的工作,所以需要把基础夯实一下. 本文是参照了joy_lee的博客 闭包 在她这篇博客的基础上以批注的形式力争把我的理解阐述出 ...
- UE4中的AI行为树简单介绍
UE4引擎中可以实现简单AI的方式有很多,行为树是其中比较常用也很实用的AI控制方式,在官网的学习文档中也有最简单的目标跟踪AI操作教程,笔者在这里只作简单介绍. AIController->和 ...
- 【转】JS回调函数--简单易懂有实例
JS回调函数--简单易懂有实例 初学js的时候,被回调函数搞得很晕,现在回过头来总结一下什么是回调函数. 我们先来看看回调的英文定义:A callback is a function that is ...
- java生成RSA公私钥字符串,简单易懂
java生成RSA公私钥字符串,简单易懂 解决方法: 1.下载bcprov-jdk16-140.jar包,参考:http://www.yayihouse.com/yayishuwu/chapter ...
- HashSet的实现原理,简单易懂
HashSet的实现原理,简单易懂 答: HashSet实际上是一个HashMap实例,都是一个存放链表的数组.它不保证存储元素的迭代顺序:此类允许使用null元素.HashSet中不允许有重复元 ...
- 用最简单的代码写出banner图轮播效果
以下视频是由[赵一鸣随笔]博客提供的“用最简单的代码写出banner图轮播效果”. 查看全屏高清视频,请点击链接:http://www.zymseo.com/58.html
随机推荐
- 如何写一个全局的 Notice 组件?
下面将会实现这样的效果: 组件动态创建脚本: NotificationBanner.js import Vue from "vue"; import Notice from &qu ...
- 全球共有多少MySQL实例在运行?这里有一份数据
摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例.因为这份报告基数够大,而且信息也非常完整,从数据库专业 ...
- Quartus II 13.0 sp1的官方下载页面
今天为了下个ModelSim跑到网上去找下载资源,清一色的百度网盘,下载速度60k/s,简直有病,于是跑到Intel官网上把连接挖出来了,供各位直接下载 实测使用IDM多线程下载速度可以轻松上到数MB ...
- 论文解读(USIB)《Towards Explanation for Unsupervised Graph-Level Representation Learning》
论文信息 论文标题:Towards Explanation for Unsupervised Graph-Level Representation Learning论文作者:Qinghua Zheng ...
- vue按需引入第三方ui插件优化
components.js import { fullScreenContainer, borderBox12, scrollBoard, loading, borderBox10, borderBo ...
- python基础知识-day9(数据驱动)
1.数据驱动的概念 在自动化测试中,需要把测试的数据分离到JSON,YAML等文件中. 2.YAML 的相关知识 YAML 入门教程 分类 编程技术 YAML 是 "YAML Ain't a ...
- 一条 SQL 语句是如何执行的
一条 SQL 语句是如何执行的 SQL查询语句 select * from user where ID=10; MySQL 的基本架构可以分为 Server 层和存储引擎两部分.Server 层又包含 ...
- ssh-免密钥登陆
实现openssh免密钥登陆(公私钥验证) 在主机A上,通过root用户,使用ssh-keygen生成的两个密钥:id_rsa和id_rsa.pub 私钥(id_rsa)保存在本地主机,公钥(id_r ...
- NC21874 好串
NC21874 好串 题目 题目描述 牛牛喜欢跟字符串玩耍,他刚刚学会了一个新操作,将一个字符串x插入另一个字符串y中(包括放在开头和结尾) 牛牛认为如果一个串是好的当这个串能按照如下方法被构造出来: ...
- 组网神器Zerotier One使用
一些问题 可以用来干嘛? 异地组网,管理方便,A.B网段内的IP可以直接相互访问 到底好不好用? 如果不搭建MOON节点,直接P2P的话,速度确实让人捉急,我感觉这个原因劝退了绝大多数人 和FRP的区 ...