线段树详解(包含加法线段树、乘法线段树及区间根号线段树,简单易懂)

十年OI一场空,不开long long见祖宗 //线段树:单点修改+区间求和 #include<bits/stdc++.h> #define ll unsigned long long using namespace std; ll n,m,a[1000010],ans[2000010],tag[2000010]; ll ls(ll x) { return x<<1; } ll rs(ll x) { return x... 阅读详情

同步发表于 优快云@return_dr

这一篇文章我们将对线段树中的常规操作进行详细的讨论。

  • 以下所提到的复杂度如无特殊说明均为时间复杂度。 log ⁡ \log log 的底数均为 2 2 2。

不开 long long 见祖宗!

第一部 普通线段树

一、引入

对于一串数,举例为 a a a,需要实现以下几种操作(修改操作均为加减法):

  1. 单点修改
  2. 单点查询
  3. 区间修改
  4. 区间查询(区间和)

显然 1 1 1 与 2 2 2 被 3 3 3 与 4 4 4 包含。但为了讨论的清晰,我们也将对其单独讨论。

如果我们用常规方式(数组)存储这串数,其查询操作为 O ( 1 ) O(1) O(1),更改操作为 O ( 1 ) O(1) O(1),求和操作为 O ( l ) O(l) O(l)( l l l 为所求序列长度)。

#define LL long long // 不开 long long 见祖宗!
#define ref(i, a, b, p) for (signed(i) = (a); (i) <= signed(b); (i) += signed(p))
const int maxn = 50005;
LL a[maxn], n, q, sum;
void work()
{
   
   
    cin >> n;
    ref (i, 1, n, 1)
        cin >> a[i];
    cin >> q;
    ref (__, 1, q, 1)
    {
   
   
        int x, k, l, r;
        cin >> x;
        if (x == 1) // 以下均如题
        {
   
   
            cin >> k;
            a[x] = k;
        }
        else if (x == 2)
            cout << a[x] << endl;
        else if (x == 3)
        {
   
   
            cin >> l >> r >> k;
            ref (i, l, r, 1)
                a[x] += k;
        }
        else
        {
   
   
            sum = 0;
            cin >> l >> r;
            ref (i, l, r, 1)
                sum += a[i];
            cout << sum << endl;
        }
    }
}

如果用前缀和来操作的话,虽然求和操作为 O ( 1 ) O(1) O(1),但是更改数值后更新前缀和的复杂度为 O ( n ) O(n) O(n),没有起到太大的优化效果,不再放代码。

有人说 O ( n ) O(n) O(n) 的时间复杂度已经很优了,但是设想,如果有 q q q 次询问,那么总的复杂度为 O ( n q ) O(nq) O(nq),不可接受。

二、优化方案

经过前面优先队列等数据结构的铺垫,我们知道,树是一种能在复杂度为 O ( log ⁡ n ) O(\log n) O(logn) 的情况下处理大部分操作的数据结构。

那么我们可以这样想:对于一串数 a a a,我们把它看成一条长度为 n n n 的线段,标记线段左端点为 1 1 1,右端点为 n n n,那么我们让线段上的 1 1 1 对应 a 1 a_1 a1​,让 2 2 2 对应 a 2 a_2 a2​……让 n n n 对应 a n . a_n. an​.,这样我们就用一条线段表示出了 a a a。

我们此时建立一棵完全二叉树(偶数个数的情况下为满二叉树),以刚刚建立的线段上的每一个点(从 a 1 a_1 a1​ 到 a n a_n an​)为叶子节点,用父节点表示其左右儿子的和,那么根节点就为整条线段(也就是整串数)的和。

具体来看(代码放到最后):
建树时,先建立叶子节点,然后逐层回溯计算父结点的值,每个节点需要存储的变量有它的值以及左右端点(就是区间范围)。时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)。

如下图,先建立节点 1 , 2 , 3 … 8 1,2,3\dots8 1,2,3…8(代表 a 1 , a 2 , a 3 … a 8 a_1,a_2,a_3\dots a_8 a1​,a2​,a3​…a8​,下同),然后逐层回溯更新节点 a , b , c … g a,b,c\dots g a,b,c…g,更改顺序:红、黄、绿、蓝。
在此图中:

a = 1 + 2 ,   b = 3 + 4 ,   c = 5 + 6 ,   d = 7 + 8. a=1+2,\,b=3+4,\,c=5+6,\,d=7+8. a=1+2,b=3+4,c=5+6,d=7+8.

e = a + b = ∑ i = 1 4 a i ,   f = c + d = ∑ i = 5 8 a i . e=a+b=\sum\limits_{i=1}^{4}a_i,\,f=c+d=\sum\limits_{i=5}^{8}a_i. e=a+b=i=1∑4​ai​,f=c+d=i=5∑8​ai​.

g = e + f = a + b + c + d = ∑ i = 1 8 a i . g=e+f=a+b+c+d=\sum\limits_{i=1}^{8}a_i. g=e+f=a+b+c+d=

线段树 (经典题目合集) P3372 【模板】线段树 1 #include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=1e5+7; const ll mod=2147483647; ll n,m; struct node { ll l,r,sum,lz; }tree[N]; ll arr[N]; void b... 阅读详情

相关推荐

算法竞赛从入门到进阶pdf_【算法趣谈】线段树 从入门到进阶

前言:这是一篇笔者两年前所写的博客。如今,在线段树在脑海中即将遗忘之际,这两年前留下的文章,终于将其再次唤醒。这篇文章对于线段树的理解虽谈不上绝对深刻,但是也能详细且简明地解释其流程。因而也收获了3w+的阅读量。今日,我也将原先的博客的语言以及配图进行了一定优化,希望能对各位读者有所帮助。“线段树是什么?线段树怎么写?”如果你在考提高组前一天还在问这个问题,那么你会与一等奖失之交...

weixin_39623713的博客 2380

【c++提高1】数据结构之线段树详解

线段树的思想是将一个区间[1, n]划分成两个区间: [1, n/2]和[n/2+1, n],接下来继续划分: [1, n/4], [n/4+1,n/2]和[n/2+1, n/2+n/4-1], [n/2+n/4,n] …示例:n = 81.pushup函数2.pushdown3.build4.modify5.query1.线段树的儿子存储2.线段树的节点信息存储 定义一个结构体,里面存储改节点信息和当前这个节点所管辖的区间的左右端点。1.建树函数build 递归建树。 怎样判断当前节点是叶子节点呢?2.查

1 2274

建议收藏:模拟版图高频面试题,直通面试,Offer抢先拿!

直击模拟版图面试

nuoweishizi的博客 6334

【STM32实战】利用STM32CubeMX配置PWM互补输出驱动电机

本文详细介绍了如何利用STM32CubeMX配置STM32高级定时器的PWM互补输出功能,以驱动电机等需要半桥/全桥电路的应用。通过图形化界面设置定时器参数、使能互补通道并配置关键的死区时间,结合代码示例演示了如何启动PWM输出和动态调整占空比,确保输出安全、精确的互补信号,避免MOS管直通风险,提升驱动可靠性。

weixin_29161785的博客 534

2025华东杯A/B/C题解题思路+可运行代码参考

这些因素包括但不限于:助滑坡的坡度、运动员的质量、滑雪板的接触面与雪地的摩擦力、空气阻力系数、运动员的体型和衣物形态等。助滑坡是运动员滑行至起跳台前的最后一段坡道,这一段坡道的坡度、长度和运动员的姿势会直接影响到起跳时的初始速度。运动员的滑行速度不仅受助滑坡的坡度影响,还受到摩擦力、空气阻力和重力等多方面因素的影响。通过合理的姿势调整,运动员能够有效减少空气阻力,增加身体的重力加速度,进而提升滑行速度。助滑坡的角度通常介于10°到12°之间,而运动员的滑行速度将受到重力、摩擦力和空气阻力的共同作用。

qq_52590045的博客 1796

Dify使用教程3:Dify创建工作流

Dify使用教程3:Dify创建工作流 接着,我们连接下一个节点:“LLM”填入上下文变量为:result。这时候知识库就添加成功了。添加新节点:结束节点。

拥抱具身智能,昇腾Ascend C 5128

MIPI D-PHY TX 一致性测试实例解析 Part 03

本文之前,已经讨论了MIPI D-PHY TX的1.3.x和1.4.x的测试项,1.5.x的测试项相对较少,主要是用于时钟和数据通道相关的时序测量

一只豌豆象的博客 5184

一分钟了解OpenSpec (规格)

OpenSpec是由Fission AI开源的轻量级规范驱动开发(SDD, Spec-Driven Development)CLI工具。它专为AI编程助手(如Cursor、Claude Code)设计,核心是**“先约定,后编码”:在AI写代码前,先通过结构化规范文档达成共识,让开发过程可预测、可审计、可协作**。轻量无侵入:单CLI、无依赖、不改造项目、1分钟接入存量友好:增量Spec、不重构旧文档、1→n王者完全可控:每步审查、AI按图施工、零返工协作利器。

h a 6 6 6 c k 为 你解决复杂难题! 2743

SEGGER Embedded Studio初体验

先楫HPM5E00EVK开发环境搭建

hywing's blog 1108

运行codex时出现登录失败:failed to start login server: 以一种访问权限不允许的方式做了一个访问套接字的尝试。 (os error 10013) 解决方案

本文主要介绍了运行codex时出现登录失败:failed to start login server: 以一种访问权限不允许的方式做了一个访问套接字的尝试。 (os error 10013) 解决方案,希望能对使用xx的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 10万+

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

上一篇: 秦九韶算法c++
下一篇: 函数知识点总结
return_dr
博客等级 码龄5年 216粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值