线段树基础与模板与简单应用

模板 - 线段树 ACM-ICPC模板 懒惰标记 一行序列,每次操作把一个区间里的每个数都加上一个数,或者查询一个区间的和。 typedef long long ll; typedef pair<int,int> PII; const int INF = 0x3f3f3f3f; const int N = 1e5+7; const int M = 2007; struct SegmentTree{ int l,r; ll sum;//总和 ll lz_add;//增量延迟标记 阅读详情

线段树

参考自http://www.cnblogs.com/TenosDoIt/p/3453089.html

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。(父节点从0开始的情况)

由于线段树的父节点区间是平均分割到左右子树,因此线段树是完全二叉树,对于包含n个叶子节点的完全二叉树,它一定有n-1个非叶节点,总共2n-1个节点,因此存储线段是需要的空间复杂度是O(n)。

实现代码

    #include<iostream>
    #include<cstdio>
    #include<cmath> 
    using namespace std;

    const int INFINITE = INT_MAX;
    const int MAXNUM = 1000;
    struct SegTreeNode
    {
        int val;                //something
        int addMark;            //延迟标记
    }segTree[MAXNUM];           //定义线段树


    /*
    功能:构建线段树
    root:当前线段树的根节点下标
    arr: 用来构造线段树的数组  就是叶子节点的值val数组 
    istart:数组的起始位置
    iend:数组的结束位置
    */
    void build(int root,int arr[],int istart,int iend)
    {
        //首先延迟标记初始为0
        segTree[root].addMark = 0;

        //因为是递归,所以有终止条件,就是叶子节点
        if(istart == iend)
            segTree[root].val = arr[istart]; 

        else
        {
            int mid = (istart + iend) / 2;       //找到划分
            build(root*2+1, arr, istart, mid);
            build(root*2+2, arr, mid+1, iend);  

            //根据左右子树的值,更新当前结点的val值  这里是模拟区间最小值 
            segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val); 
        }
    }

    /*
    功能:当前节点的标志域向孩子节点传递
    root: 当前线段树的根节点下标
    */
    void pushDown(int root)
    {
        /*所谓延时标记,就是说我们想要更新某个区间的所有值,那么就到某个区间就好,
        不用去叶子节点逐一更新再逐一回溯。只需要在某中间结点上标记(就更新完毕),那么下次如果要
        访问该结点的叶子节点时,再去更新这些叶子节点(同时取消该结点延时),就可以了,也就是我们需要的时候
        再更新结点的值*/ 

        if(segTree[root].addMark != 0)
        {
             //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递所以+= 
            segTree[root*2+1].addMark += segTree[root].addMark;
            segTree[root*2+2].addMark += segTree[root].addMark;

            //这里是模拟val是区间最小值,所以就要值++
            segTree[root*2+1].val += segTree[root].addMark;
            segTree[root*2+2].val += segTree[root].addMark;

            //传递后把root结点的标记清空
            segTree[root].addMark = 0; 
        }
    }

    /*
    功能:线段树的区间查询
    root:当前线段树的根节点下标
    [nstart, nend]: 当前节点所表示的区间
    [qstart, qend]: 此次查询的区间
    */
    int query(int root, int nstart, int nend, int qstart, int qend)
    {
        //查询区间与结点区间没有交集
        if(qend < nstart || qstart > nend) 
            return INFINITE; 

        //当前节点区间完全在查询区间内
        if(qstart <= nstart && qend >= nend)
            return segTree[root].val;

        //有交集,分别在左右子树查询
        pushDown(root);                   //因为涉及到要用子结点的值了,那么延迟的就要更新

        int mid = (nstart + nend) / 2;
        return min(query(root*2+1,nstart,mid,qstart,qend),query(root*2+2,mid+1,nend,qstart,qend));  
    }

    /*
    功能:更新线段树中某个区间内叶子节点的值
    root:当前线段树的根节点下标
    [nstart, nend]: 当前节点所表示的区间
    [ustart, uend]: 待更新的区间
    addVal: 更新的值(原来的值加上addVal)
    */
    void update(int root,int nstart,int nend,int ustart,int uend,int addVal)  //以增加值为例
    {
        //没有交集
        if(ustart > nend || uend < nstart)
            return ;

        //完全在内 
        if(ustart <= nstart && uend >= nend )
        {
            segTree[root].addMark += addVal;     //就跟新这个区间  不往下更新了 延时 
            segTree[root].val += addVal;
            return;
        }

        //涉及到子树了,延迟生效
        pushDown(root);
        //更新左右结点
        int mid = (nstart + nend) / 2;
        update(root*2+1, nstart, mid, ustart, uend, addVal);
        update(root*2+2, mid+1, nend, ustart, uend, addVal);

        //根据左右子树的值回溯更新当前节点的值
        segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
    } 

线段树应用

描述

Lily特别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易。她把她的花依次排成一行,每盆花都有一个美观值。如果Lily把某盆花照料的好的话,这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降。有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily
的算术不是很好,你能快速地告诉她结果吗?

输入

第一行一个整数T,表示有T组测试数据。
每组测试数据的第一行为一个正整数N (N<=50000),表示Lily有N盆花。
接下来有N个正整数,第i个正整数ai (1<=ai<=50) 表示第i盆花的初始美观值。
接下来每行有一条命令,命令有4种形式:
(1)Add i j, i和j为正整数,表示第i盆花被照料的好,美观值增加j (j<=30)
(2)Sub i j, i和j为正整数,表示第i盆花被照料的不好,美观值减少j (j<=30)
(3)Query i j, i和j为正整数,i<=j,表示询问第i盆花到第j盆花的美观值之和
(4)End,表示结束,这条命令在每组数据最后出现
每组数据的命令不超过40000条

输出

对于第i组数据,首先输出”Case i:”和回车。
对于每个”Query i j”命令,输出第i盆花到第j盆花的美观值之和。

样例输入

1
9
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End

样例输出

Case 1:
13
30
50

思路

  • 此题为线段树模板题,注意之前的线段树模板是结点存区间最小值,现在换成区间的和即可。
  • 注意线段树的数组开大一点,此题开了4倍。
  • 注意代码中的延迟更新没必要,因为此题是单点更新,其实并没有用到,懒得改模板。

AC代码

    #include<iostream>
    #include<cstdio>
    #include<cmath> 
    #include<cstring>
    using namespace std;

    const int INFINITE = INT_MAX;
    const int MAXNUM = 200010;
    int arr[MAXNUM];
    struct SegTreeNode
    {
        int val;                //something 区间和 
        int addMark;            //延迟标记
    }segTree[MAXNUM];           //定义线段树


    /*
    功能:构建线段树
    root:当前线段树的根节点下标
    arr: 用来构造线段树的数组  就是叶子节点的值val数组 
    istart:数组的起始位置
    iend:数组的结束位置
    */
    void build(int root,int istart,int iend)
    {
        //首先延迟标记初始为0
        segTree[root].addMark = 0;

        //因为是递归,所以有终止条件,就是叶子节点
        if(istart == iend)
            segTree[root].val = arr[istart]; 

        else
        {
            int mid = (istart + iend) / 2;       //找到划分
            build(root*2+1, istart, mid);
            build(root*2+2, mid+1, iend);  

            //根据左右子树的值,更新当前结点的val,区间的和 
            segTree[root].val = segTree[root*2+1].val + segTree[root*2+2].val; 
        }
    }

    /*
    功能:当前节点的标志域向孩子节点传递
    root: 当前线段树的根节点下标
    */
    void pushDown(int root)
    {
    //  cout<<"pushDown"<<endl;
        /*所谓延时标记,就是说我们想要更新某个区间的所有值,那么就到某个区间就好,
        不用去叶子节点逐一更新再逐一回溯。只需要在某中间结点上标记(就更新完毕),那么下次如果要
        访问该结点的叶子节点时,再去更新这些叶子节点(同时取消该结点延时),就可以了,也就是我们需要的时候
        再更新结点的值*/ 

        if(segTree[root].addMark != 0)
        {
             //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递所以+= 
            segTree[root*2+1].addMark += segTree[root].addMark;
            segTree[root*2+2].addMark += segTree[root].addMark;

            //这里是模拟val是区间最小值,所以就要值++
            segTree[root*2+1].val += segTree[root].addMark;
            segTree[root*2+2].val += segTree[root].addMark;

            //传递后把root结点的标记清空
            segTree[root].addMark = 0; 
        }
    }

    /*
    功能:线段树的区间查询
    root:当前线段树的根节点下标
    [nstart, nend]: 当前节点所表示的区间
    [qstart, qend]: 此次查询的区间
    */
    int query(int root, int nstart, int nend, int qstart, int qend)
    {
    //  cout<<"query("<<root<<")"<<endl;
        //查询区间与结点区间没有交集
        if(qend < nstart || qstart > nend) 
            return 0; 

        //当前节点区间完全在查询区间内
        if(qstart <= nstart && qend >= nend)
            return segTree[root].val;

        //有交集,分别在左右子树查询
        pushDown(root);                   //因为涉及到要用子结点的值了,那么延迟的就要更新 

        int mid = (nstart + nend) / 2;
        return query(root*2+1,nstart,mid,qstart,qend) + query(root*2+2,mid+1,nend,qstart,qend);  
    }   

    /*
    功能:更新线段树中某个区间内叶子节点的值
    root:当前线段树的根节点下标
    [nstart, nend]: 当前节点所表示的区间
    [ustart, uend]: 待更新的区间
    addVal: 更新的值(原来的值加上addVal)
    */
    void update(int root,int nstart,int nend,int ustart,int uend,int addVal)  //以增加值为例
    {
        //没有交集
        if(ustart > nend || uend < nstart)
            return ;

        //完全在内 
        if(ustart <= nstart && uend >= nend )
        {
            segTree[root].addMark += addVal;     //就跟新这个区间  不往下更新了 延时 
            segTree[root].val += addVal;
            return;
        }

        //涉及到子树了,延迟生效
        pushDown(root);
        //更新左右结点
        int mid = (nstart + nend) / 2;
        update(root*2+1, nstart, mid, ustart, uend, addVal);
        update(root*2+2, mid+1, nend, ustart, uend, addVal);

        //根据左右子树的值回溯更新当前节点的值
        segTree[root].val = segTree[root*2+1].val + segTree[root*2+2].val;
    } 

    int main()
    {
        int T;
        scanf("%d",&T);
        int Case = 1;
        char com[11];
        while(T--)
        {
            printf("Case %d:\n",Case++);
            memset(arr,0,sizeof(arr));
            int n;
            scanf("%d",&n);

            //此模板是的根节点标号是0
            for(int i = 0; i < n ;i ++)
                scanf("%d",&arr[i]);

            build(0,0,n-1);

    //      for(int i=0;i<=17;i++)
    //          cout<<segTree[i].val<<" ";

            int i,j;
            while(scanf("%s",com),com[0] != 'E')
            {   
                scanf("%d%d",&i,&j);
                if(com[0] == 'A')
                {
                    update(0,0,n-1,i-1,i-1,j);
                }

                else if(com[0] == 'S')
                {
                    update(0,0,n-1,i-1,i-1,-j);
                }

                else if(com[0] == 'Q')
                {
                    int ans = query(0,0,n-1,i-1,j-1);
                    printf("%d\n",ans);
                }           
            }
        }
        return 0;
    } 

ROS新手必看:5分钟搞定URDF小车建模(附Rviz可视化调试技巧) 本文为ROS新手提供了快速构建URDF机器人模型的实战指南。通过一个完整的两轮差速小车建模案例,详细解析了URDF的核心语法、Rviz可视化调试技巧以及常见问题排查方法,帮助初学者快速掌握ROS机器人建模与可视化调试的核心技能。 阅读详情

相关推荐

学术报告系列(二) - 智能自主作业机器人感知与控制技术及发展趋势

收集整理于2022年模式识别高峰论坛湖南大学王耀南院士报告(报名题目:智能自主作业机器人感知与控制技术及发展趋势)

Make something people want. 4696

线段树详解(附图解,模板及经典例题分析)

线段树是一种常用来维护区间信息的数据结构 ​ 可以在 O(logn) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间的最大值,求区间的最小值)等操作 ​ 如下图所示,线段树是建立在区间基础上的树,树的每个节点都代表着一段区间【L,R】

qq_63786973的博客 3896

压电阀控制器设计-总章

本文章用于回顾记录本人制作压电阀控制器的关键技术,本人所制作压电阀控制器已经上市得到应用,特在此对一些关键技术进行回顾记录,欢迎从事压电控制及压电阀相关人员一起讨论

qq_42712859的博客 849

线段树模板

线段树 打标记

听取WA声一片 2341

2026四款AI原生IDE实测:Trae、Qoder、CodeBuddy、Cursor能力边界深度解析

AI原生IDE正从代码编辑器演进为开发者智能工作流中枢,其核心在于对上下文理解、AST编辑意图建模与多模态任务调度的工程化实现。区别于传统插件式AI辅助,真正的AI原生IDE需具备知识库嵌入、符号图索引、操作链固化等底层能力,从而支撑Figma转码、N+1查询修复、跨语言迁移等复杂场景。Trae强调中文语境与企业规范的强绑定,Qoder聚焦AST级编辑轨迹预测,CodeBuddy通过IDE/插件/CLI三态划分控制权边界,Cursor则以云端Agent实现开发任务的时空并行。本文基于真实项目压测,揭示免费版

ctk87443的博客 2450

MATLAB 论文复现——含氢综合能源系统多目标最优折中分布鲁棒低碳调度

提出基于多目标决策理论和逼近理想解排序技术,构造综合考虑系统调度的经济性、低碳性和鲁棒度的多目标最优折中函数,从而建立含氢综合能源系统多目标最优折中分布鲁棒低碳调度模型,可快速准确地获得兼具经济性、低碳性和鲁棒性的含氢综合能源系统优化调度方案。[1]孙惠娟,胡紫平,彭春华,等.含氢综合能源系统多目标最优折中分布鲁棒低碳调度[J/OL].电力自动化设备,1-13[2024-07-26].https://doi.org/10.16081/j.epae.202406003.关键词: 综合能源系统;

sjr1478的博客 410

HDU 1166 敌兵布阵

Description Lily特别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易。她把她的花依次排成一行,每盆花都有一个美观值。如果Lily把某盆花照料的好的话,这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降。有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily的算术不是很好,你能快速地告诉她结果吗? Inp...

java开发指南博客 【转载】 238

避开STM32H7的‘保护陷阱’:从Option Byte机制深度解析读写保护原理与预防

本文深度解析STM32H723VGT6的Option Byte保护机制,详细讲解RDP读保护和WRP写保护的工作原理及配置方法,并提供硬件设计防御策略和软件预防性编程技巧,帮助开发者避免误触发保护机制导致芯片锁定的问题。

weixin_33686714的博客 441

线段树板子

这是一个纯纯的,水水的线段树板子 以下是CODEVS1080代码 ……

博客迁移http://www.cnblogs.com/xzz_233/ 454

madgwick算法基于MPU9250姿态融合漂移问题的原因

基于MPU9250的madgwick算法姿态漂移问题原因 0、声明 因为这个是现象是不是共性目前本人还没研究过,只是在个体问题研究的基础上得出的结论,或许我买到的MPU9250是一个盗版:),所以只具备参考性。 1、现象 使用MPU9250的加速度计、角速度计、磁力计(AK8963)原始数据输入到madgwick算法中,得到的姿态最终会在停止姿态变化后仍然漂移一小段时间。其中加速度计和角速度计读取速率为400hz,磁力计为100hz,加速度计采样范围±2g,角速度计采样范...

GreedyZombie的博客 3872

github创建账号第一步始终出错

github创建账号总结:第一次注册GitHub,填好用户名邮箱密码后进入下一步就卡在哪里,上面还提示Unable to verify your captcha response等,进入网址的解决办法以及下载其他的浏览器也无法解决,用手机浏览器抱着试一试的心态竟然成功了,分享给大家。手机下载谷歌浏览器,进入GitHub正常注册,能向下继续,邮箱验证完以后再用电脑登陆就ok啦

计算机毕业论文源码,学生个人网页制作html源码。贴近用户做网络推广和互联网优化。 4373

浅谈线段树(Segment Tree)

线段树的定义百度百科定义如下: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 由定义可知,线段树实质上是一种二叉搜索树,是一棵完全二叉树,它们的各个节点保存着一个线段的信息(这个信息可以是最值、区间和等多种信息)。线段树的基本内容下图是一棵区间长度为10的线段树一般的,我们把线段树从上到下从左到右开始编号,例如:[1,10]编号为0

CoolKid_cwm的博客 2117

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

hdu 1754 I Hate It(数据结构:线段树)

基础线段树应用,把模板shaoweigaixia

小华在努力~ 419

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

线段树

很长,但此模板基本上把线段树所有的功能都包括了,包括更新,查询区间等,并且提前就把和还有最大最小值都写上了,到时候只需要去掉某些部分,并且把下面的部分内容改一下就好: 给一道模板题自己看看吧: 猛戳这里 代码如下: //线段树模板 struct node{ int r,l,sum,MAX,MIN; }a[1000<<2];//存树 void pushup(int o)//更新数据

进击的小白 232

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

ACM模板——区间问题(线段树 RMQ-ST)模板

非常好用的线段树模板!

Kiritow的学园 634

安利一下近期做的几道线段树入门题

写了几道线段树入门题以后的我感觉会了点东西~ 放几道有代表性的。 P.S.洛谷的题号 P3372模板 这个不用说了。 P1558色板游戏 状压,区间或,裸题。 P2471降雨量 思路不难,细节很多。 P2572序列操作 题意: 长度为n的01序列,m次操作 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把...

weixin_30636089的博客 86

解题报告:SP2713 GSS4 - Can you answer these queries IV(GSS线段树八部曲之四)

x>yx>yx>y是真的艹。 然后就是一道模板题了。 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 500007; int n, m; ll a[N]; struct Tree{ int l, r; ll sum; }tr[N * 4]; void pushup(int p){ tr[p].sum = tr[p << 1.

繁凡さん的博客 415

[笔记]线段树 | 树状数组

线段树 最Simple的区间加码风样式模板 struct Segment_Tree { #define ls (p << 1) #define rs (p << 1 | 1) #define mid ((l + r) >> 1) ll ans[_ << 2], tag[_ << 2]; inline...

weixin_30394981的博客 79

hdu - 1556 (Color the ball 线段树)

题目链接:点击打开链接 思路:简单的线段树模板,理解了好久。创建+更新+查询。更新只涉及到区间更新次数(+1)即可。查询可以递归查询,累加包含查询结点的树结点(即离散化的区间[l,r])的次数即可。简单的说就是,查询结点所在的区间更新次数的总和。 eg:离散化到树结点,若共有4个结点,更新区间[1,3]则更新到区间[1,2]+[3,3]. #include <iostream> ...

weixin_33753003的博客 122

【简单算法】2022SCUACM集训队冬季选拔全题解

没想到现在冬季选拔都这么难了……题目传送门本文juejin:https://juejin.cn/post/7222531019319722039/作者:hans774882968以及hans774882968以及hans774882968参考:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc165.html最长严格上升子序列树上版本。经典问题有两种做法:树状数组、维护数组+二分查找。但把问题搬到树上后,我们遇到一个问题:在回溯的时候,需要对数据结构进行撤销操作。

hans774882968的博客 948

hdu 1166 排兵布阵

/* *********************************************** Author :xryz Email :523689985@qq.com Created Time :4-22 20:46:48 File Name :hdu排兵布阵.cpp *******************************...

weixin_30344131的博客 167

SDAU寒假训练一

今天的训练赛。。没好好打。。就看了两个小时的题,然后也没有敲代码,倒是看到两个会做的EG,一个dp,一个线段树,这两个还是比较简单的赶脚。 A题据说是个数学的模板题,查无此模板。。。没学这东西,所以是不会的。 再就是并查集那个题,H,感觉我自己写的,直接dfs判集合属于并不超时,但是结果是超时的,这就有点迷了,非得用并查集做。。 I题是个思维技巧用set暴力枚举区间可还行,,,也是水的有态度...

AAAAAAAC的博客 226

线段树的原理与模板

如果会树状数组的同学应该就很容易理解线段树了,在一定程度上,两者是有一点类似的。首先,了解一下我们为什么要使用线段树,以及线段树的主要作用。 区间求和问题-医院卖药 假设有一家医院,医院有卖药的地方,不同的药品有不同的数量。每次开药、进药都要在计算机里面记录数量变化,这样方便医院的管理。那么我们该如何实现这样的程序?当然,药品数量的储存用数组是比较合适的(真实的...

iwts 1万+

线段树 从入门到进阶(超清晰,简单易懂)

目录第一部 概念引入第二部 简单(无pushdown)的线段树1、单点修改,区间查询2、区间修改,单点查询第三部 进阶线段树第四部 乘法(根号)线段树1、乘法线段树2、根号线段树模板题与代码:单点修改,区间查询:洛谷树状数组模板1区间修改,单点查询:洛谷树状数组模板2区间加法,洛谷线段树模板1区间乘法:洛谷线段树模板2区间根号,bzoj3211 线段树是什么??线段树怎么写?? 如果你在考提高组前...

繁凡さん的博客 12万+

【线段树模板以及Codeforces线段树专题总结】

线段树模板 //线段树一般用来解决可合并的区间问题 //树上维护子树区间的问题也可以通过dfs序+线段树去维护 //不要忘记build,dfs序问题build时注意下标 //数组记得开4倍,push_down一定要想好顺序 //对于每个问题,我们只要考虑清楚push_down和push_up的写法即可 //当问题不好处理的时候,想象一下对于单次询问暴力的做法,再进行区间操作 struct T { ...

lajiyuan的博客 4090
上一篇: hdu6130_Kolakoski_模拟递推
下一篇: 2017CCPC网络选拔赛1005CaoHaha's staff(规律贪心)
WaveBridge
博客等级 码龄10年 8粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值