非递归线段树实现

ZKW 非递归线段树

参考文章:线段树详解(非递归版)_非递归线段树-CSDN博客

建树:

原数组[1,n] 存在线段树的 [2,n+1] (为了方便区间查询,要空出第一个节点和最后一个节点)

区间查询

若查询[L,R] 区间, 选取L-1和 R+1 两个节点, 向上寻找.

  • 每次到达父节点 L-1 查看自己的父亲的右节点是否是R+1(或者自己是不是右节点),
    • 如果不是则加上右节点的 val .
  • R+1 节点则查看自己的父亲的左节点是否是 L-1(或者自己就是左节点),
    • 如果不是 加上左节点的 val .

线段树填充

若该线段树可维护 N 个元素

如果该线段树是满二叉树, 那么会有

​ 线段树下标 + N -1 = 存储下标

而之前为了方便区间查询有:

​ 原数组下标 + 1 = 线段树下标

那么如何确定 N 值?

​ N 是大于等于n +2 的,且由于要构造满二叉树, N 是 2 的幂次方

存在Lazytag 时该怎么进行区间查询?

​ 非递归是从下往上进行查询的

  • 如果题目允许, 可以先打上标记,所有标记都打完之后一次下推所有标记, 然后再开始查询

  • 如果标记和查询交错, 那么可以采用标记永久化的方法, 也就是不下推标记.

    L-1 与 R+1 节点往上查询的时候, 多带上一个变量, 记录此时已经计算了多少个数,当该节点达到了一个存在 tag标记的节点的时候, 加上tag 对各个数的影响,然后一次一路寻找到根节点, 返回答案

区间修改

#define maxn 100009
int a[maxn];
int sum[maxn<<2];//  区间和
int add[maxn<<2]; // 区间加lazytag
int n; // 原数组元素
int N: // N为扩充元素个数
#define ls(p)	(p<<1)
#define rs(p)	(p<<1|1)


建树

void build(int n){
    N =1 ;while(N<n+2)N<<=1;
    for(int i =1 ;i<=n;i++)sum[N+i] = a[i];// 直接修改叶子节点标记, 注意因为形成满二叉树, 其实有一些节点是空节点, 为0;
    for(int i= N -1 ;i>0;i--){// 更新根节点
		sum[i] = sum[ls(i)] + sum[rs(i)];
    	add[i] = 0;
	}
}

点修改

void update(int l,int c){
   	for(int i = N+l;i>0;i>>=1){
        sum[i] += c;
	}
}

区间修改(无tag, 单点修改版本)


void change(int l ,int r){
    l=l+N-1,r=r+N+1;
    // l^s^1 == 0 就代表 l 与 s 相邻, 也就是所有区间统计的内容都已经取完了
    int ans =0;
	while(l^s^1){
       if(~l&1) ans += sum[l^1]; //如果l 是左节点
       if(r&1)  ans += sum[r^1]; //如果r 是右节点
        l >>= 1;r >>= 1; 		// 寻找父亲节点
	}
}

注意l ^r ^1 = 0 除了存在相同的父节点还有一种情况,当然这两种情况都是可以直接退出的,可以所有打上的标记都已经统计完了:

图片1

区间修改(区间加版本)

void changeblock(int l,int r,int z){
    int i,j,s1=0,s2=0,x=0;
	for(i=l+N-1,j=r+N+1;i^j^1;i>>=1,j>>=1){
            sum[i] += s1 * z; sum[j] += s2 * z;
        	if(~i&1) add[i^1] += z, sum[i^1]+= s1*z,s1+=x;// x 为包括的节点数
            if(j&1)  add[j^1] += z , sum[j^1]+=s2*z, s2+=x;
	}
    for(;i;i>>=1,j>>=1){// 更新上层
        sum[s] += s1* z;
        sum[t] += s2* z;
	}
}


区间查询(tag 版本, 区间加版本)

void query(int l,int r){
    int ans =0,i,j,s1,s2,x=1;
	for(i= l+ N-1,j = l+ N +1;i^j^1;i>>=1,j>>=1,x<<=1){
		if(add[i]) ans += s1*add[i];
         if(add[j]) ans += s2*add[j];
         if(~i&1) ans += sum[i^1],s1+=x;
         if(j&1)  ans += sum[j^1],s2+=x;
    }
    for(;i;i>>=1,j){
        if(add[i])ans+= s1 *add[i];
        if(add[j])ans++ s2 *add[j]
    }
    return ans ;
}
posted @   -风间琉璃-  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示