zkw线段树
本文网址:https://www.cnblogs.com/zsc985246/p/16112689.html ,转载请注明出处。
zkw线段树就是非递归的线段树。
我们先来看看普通的线段树:
换成二进制看看?
这种存储方式及这三条规律便是zkw线段树的核心原理。
建树
我们通过上图可以发现,我们令 ,只需要从 开始遍历,到 结束,就可以成功的建树了。
接下来考虑维护其它节点的信息。可以直接从上图看出, 其实是第一个叶子节点,而所有的非叶子节点下标都小于 。通过这一点,我们可以直接从 遍历到 访问其它节点并维护信息。
void build(){//建树
while(m<=n)m<<=1;//1+1<<ceil(log(n))
for(int i=m+1;i<=m+n;i++){
scanf("%d",sum[i]);//这里维护区间和
}
for(int i=m-1;i;i--){//遍历非叶子节点
sum[i]=sum[i<<1]+sum[i<<1|1];//维护
}
}
单点修改
第三条规律:修改第 个元素对应到线段树上就是修改下标为 的数。
第二条规律:我们可以用 找到 的父亲,然后维护信息。
void change_point(int x,int pos){//a[x]=pos
x+=m;//找对应叶子节点
sum[x]=pos;//更新
for(x>>=1;x;x>>=1){//向上更新
sum[x]=sum[x<<1]+sum[x<<1|1];//维护
}
}
区间修改(区间加法)
跟普通的线段树差别不大,也是开一个 数组,记录这颗子树的加法操作。
但是,我们这是非递归,所以我们需要换种方式。
首先我们先把这个闭区间 转换成开区间 。
转换成开区间 后,令 。
如果 是它父亲的左儿子,就更新它的兄弟节点 ,然后 再跳到父亲节点,重复执行此操作。
同理。
注意同时进行 和 的操作。
借助图理解一下(红色代表 需要更改):
然后对红色部分的 更改即可。
等等,好像没有下传标记——
实际上,我们不需要下传标记。我们可以让标记永久化。在询问时,如果此节点的 有值,答案加上 区间长度 的值 即可。
注意,当 和 的父亲相同时,虽然不继续向上跳了,但还需要继续维护 的值。
void change_part(int l,int r,int pos){//a[s~t]+=pos
int s=l+m-1,t=r+m+1;//s和t
int len=1;//当前层区间包含数的个数
int lcnt=0;//当前s的子树中更改的数的个数
int rcnt=0;//当前t的子树中更改的数的个数
while(s^t^1){//t^1是t的兄弟节点,因为a^a=0,所以s^t^1=0代表s和t父亲相同
if(s&1^1){//s&1取末位(0为左儿子,1为右儿子),再^1表示兄弟节点
add[s^1]+=pos;//打标记
lcnt+=len;//更改了len个数
}
if(t&1){//原理与上相同
add[t^1]+=pos;//打标记
rcnt+=len;//更改了len个数
}
//维护
sum[s>>1]+=pos*lc;
sum[t>>1]+=pos*rc;
s>>=1,t>>=1;//跳到父亲节点
len<<=1;//包含的节点个数*2
}
int cnt=r-l+1;//总共更改的数的个数
s>>=1;//父节点
while(s){
sum[s]+=cnt*pos;//维护
s>>=1;//父节点
}
}
单点查询
这不用多说,直接返回即可。
int query_point(int x){
x+=m;
return sum[x];
}
区间查询
与区间修改思路相同。
主要处理在区间修改时提到的标记永久化。
由于标记是打在一颗子树上,所以整颗子树总共加了 区间长度 的值 。
ll query(int l,int r){//区间求和
ll tmp=0;//临时答案
int s=l+m-1,t=r+m+1;//s和t
int len=1;//当前层区间包含数的个数
int lcnt=0;//当前s的子树中更改的数的个数
int rcnt=0;//当前t的子树中更改的数的个数
while(s^t^1){
if(s&1^1){
tmp+=sum[s^1]+len*add[s^1];//统计
lcnt+=len;//更改了len个数
}
if(t&1){
tmp+=sum[t^1]+len*add[t^1];//统计
rcnt+=len;//更改了len个数
}
s>>=1,t>>=1;
len<<=1;
}
int cnt=lcnt+rcnt;//总共更改的数的个数
s>>=1;//父节点
while(s){
tmp+=add[s]*cnt;//统计
s>>=1;//父节点
}
return tmp;
}
zkw线段树与线段树的对比
代码长度 | 理解难度 | 时间 | 空间 | |
---|---|---|---|---|
zkw线段树 | 短 | 稍高 | 快 | 小 |
线段树 | 长 | 低 | 稍慢 | 稍大 |
%%%zkw !
尾声
如果你发现了问题,你可以直接回复这篇文章!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!