线段树模板

news2024/12/21 10:13:52

好文分享:【数据结构】线段树(Segment Tree) - 小仙女本仙 - 博客园

线段树和树状数组的基本功能都是在某一满足结合律的操作(比如加法,乘法,最大值,最小值)下,O(logn)的时间复杂度内修改单个元素并且维护区间信息。

不同的是,树状数组只能维护前缀“操作和”(前缀和,前缀积,前缀最大最小),而线段树可以维护区间操作和。

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N4N的数组以免越界,因此有时需要离散化让空间压缩。

线段树操作:1.单点修改       

只要不断修改这个区间及其父区间即可,而不会影响其他区间情况,时间复杂度O(logn)

2.区间查询(最小值,最小值出现次数等某些)

比如要查询【2,5】这个区间,我们看看能不能将这个区间拆成线段树上的若干段区间。从根节点开始看,【2,5】与【1,7】没有什么关系,所以往下看,查询【2,5】与【1,4】的交【2,4】,查询【2,5】与【5,7】的交【5,5】,然后再继续往下递归,直到有满足的区间或者有孤立的节点了,然后再不断返回即可

例题:P3372 【模板】线段树 1

本题要进行区间加 和 区间查询操作, 其实这个树状数组改一改也能做,开两个树状数组就行了,这里采用线段树操作, 直接上板子。


#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 2E5 + 5;
int n, q, a[N];
const int mod = 1e9 + 7;
 
struct tag {
    ll mul, add;
};
tag operator + (const tag &t1, const tag &t2) {
    // (mul1 + add1) * mul2 + add2;
    return {t1.mul * t2.mul , (t1.add * t2.mul % mod + t2.add)}; 
}
struct node {
    tag t;
    ll val;
    int sz;
}seg[N * 4];
 
 
void update(int id) {
    seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
}
void build(int id, int l, int r) {
    seg[id].t = (tag){1, 0};
    seg[id].sz = r - l + 1;
    if(l == r){
        seg[id].val = a[l];
    } else {
        int mid = (l + r) / 2;
        build(id * 2, l, mid);
        build(id * 2 + 1, mid + 1, r);
        update(id);
    }
}
void settag(int id, tag t) {
    seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
    seg[id].t = seg[id].t + t;
}
 
void pushdown(int id) {
    if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        seg[id].t.mul = 1;
        seg[id].t.add = 0;
    }
}
void modify(int id, int l, int r, int ql, int qr, tag t) {
    if(l == ql && r == qr) {
        settag(id, t);
        return;
    }
    pushdown(id);
    int mid = (l + r) / 2;
    if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
    else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
    else modify(id * 2, l, mid, ql, mid, t),
        modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
    update(id);
}
 
ll query(int id, int l, int r, int ql, int qr) {
    if(l == ql && r == qr) {
        return seg[id].val;
    }
    pushdown(id);
    int mid = (l + r) / 2;
    if(qr <= mid) return query(id * 2, l, mid, ql, qr);
    else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    else return (query(id * 2, l, mid, ql, mid) +
        query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}
int main(){
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    build(1, 1, n);
    while(q--) {
        int ty; scanf("%d", &ty);
        if(ty == 1) {
            int l, r, d;
            scanf("%d %d %d", &l, &r, &d);
            modify(1, 1, n, l, r, (tag){1, d});
        } else {
            int l, r; scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, 1, n, l, r));
        }
    }
 
    return 0;
}

例题2:P3373 【模板】线段树 2

这里看到需要好多操作, 我们肯定要利用标记去做这些操作, 我们可以让标记记录 + 和 * 两种操作, 然后将所有的修改变成这俩个操作。 如:

  • 将某区间每一个数乘上 x,那么就是*x + 0,

  • 将某区间每一个数加上 x,  那么就是*0 + x;

  • 求出某区间每一个数的和,  直接无脑query

  • 
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    typedef pair<int, int> PII;
    #define pb push_back
    const int N = 2E5 + 5;
    int n, q, a[N];
    const int mod = 1e9 + 7;
     
    struct tag {
        ll mul, add;
    };
    tag operator + (const tag &t1, const tag &t2) {
        // (mul1 + add1) * mul2 + add2;
        return {t1.mul * t2.mul, (t1.add * t2.mul + t2.add)}; 
    }
    struct node {
        tag t;
        ll val;
        int sz;
    }seg[N * 4];
     
     
    void update(int id) {
        seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
    }
    void build(int id, int l, int r) {
        seg[id].t = (tag){1, 0};
        seg[id].sz = r - l + 1;
        if(l == r){
            seg[id].val = a[l];
        } else {
            int mid = (l + r) / 2;
            build(id * 2, l, mid);
            build(id * 2 + 1, mid + 1, r);
            update(id);
        }
    }
    void settag(int id, tag t) {
        seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
        seg[id].t = seg[id].t + t;
    }
     
    void pushdown(int id) {
        if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
            settag(id * 2, seg[id].t);
            settag(id * 2 + 1, seg[id].t);
            seg[id].t.mul = 1;
            seg[id].t.add = 0;
        }
    }
    void modify(int id, int l, int r, int ql, int qr, tag t) {
        if(l == ql && r == qr) {
            settag(id, t);
            return;
        }
        pushdown(id);
        int mid = (l + r) / 2;
        if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
        else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
        else modify(id * 2, l, mid, ql, mid, t),
            modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
        update(id);
    }
     
    ll query(int id, int l, int r, int ql, int qr) {
        if(l == ql && r == qr) {
            return seg[id].val;
        }
        pushdown(id);
        int mid = (l + r) / 2;
        if(qr <= mid) return query(id * 2, l, mid, ql, qr);
        else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
        else return (query(id * 2, l, mid, ql, mid) +
            query(id * 2 + 1, mid + 1, r, mid + 1, qr));
    }
    int main(){
        scanf("%d %d", &n, &q);
    
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        build(1, 1, n);
        while(q--) {
            int ty; scanf("%d", &ty);
            if(ty <= 3) {
                int l, r, d;
                scanf("%d %d %d", &l, &r, &d);
                if(ty == 1) modify(1, 1, n, l, r, (tag){1, d});
                else if (ty == 2) modify(1, 1, n, l, r, (tag){d, 0});
                else modify(1, 1, n, l, r, (tag){0, d});
            } else {
                int l, r; scanf("%d %d", &l, &r);
                printf("%lld\n", query(1, 1, n, l, r));
            }
        }
     
        return 0;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python回归预测建模实战-支持向量机预测房价(附源码和实现效果)

机器学习在预测方面的应用&#xff0c;根据预测值变量的类型可以分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff0c;前面我们介绍了机器学习建模处理了分类问题&#xff08;具体见之前的文章&#xff09;&#…

x86 --- 任务隔离特权级保护

程序是记录在载体上的数据和指令。 程序正在执行时的一个副本叫做任务 所有段描述符都放在GDT --> 不做区分。 内核程序&#xff08;任务&#xff09;所占段在GDT中&#xff0c;用户程序&#xff08;任务&#xff09;所占段在LDT中 --> 做区分。 每个任务都有自己独立的…

【无标题】

第1章 概述 本章主要内容&#xff1a; 互联网的概念&#xff08;标准化&#xff09;、组成、发展历程&#xff1b;电路交换的基本概念、分组交换的原理&#xff1b;计算机网络的分类、性能指标及两种体系结构。 重点掌握&#xff1a; 在计算机网络分层模型中&#xff0c;网…

7、GC日志详解

目录如何分析GC日志参数配置程序运行GC日志打印解析GC日志数据分析指定其他垃圾收集器CMSG1GC分析工具JVM参数汇总查看命令如何分析GC日志 参数配置 对于java应用我们可以通过一些配置把程序运行过程中的gc日志全部打印出来&#xff0c;然后分析gc日志得到关键性指标&#xff…

目标检测算法——遥感影像数据集资源汇总(附下载链接)

关注”PandaCVer“公众号 深度学习资料&#xff0c;第一时间送达 目录 一、用于 2-5 分类问题 1.UCAS-AOD 遥感影像数据集 2.Inria Aerial Image Labeling Dataset 3.RSOD-Dataset 物体检测数据集 二、用于 5-10 分类问题 1.RSSCN7 DataSet 遥感图像数据集 2.NWPU…

孙宇晨接受韩国媒体专访:熊市受宏观经济的不确定性影响

10月27日至10月29日&#xff0c;韩国釜山备受关注的大型区块链活动 2022 釜山区块链周&#xff08;BWB 2022&#xff09;在釜山会展中心&#xff08;BEXCO&#xff09;举行。韩国区块链媒体TokenPost 对出席活动的波场TRON创始人孙宇晨进行了专访。10月28日&#xff0c;该媒体发…

Nginx快速入门部署前端项目

目录 一&#xff0c;Nginx简介 1.1 负载均衡 演示 1.1.2 安装nginx 再复制一份一样的tomcat并修改端口号 打开两个tomcat的服务 打开防火墙中的8081端口 修改Nginx配置 重启Nginx服务&#xff0c;让配置生效 1.2 反向代理 Nginx项目部署 1.确保前端项目能用 2.将前台项目…

看过来,Windows 11 Insider Preview 25231.1000推送啦!

微软于近日凌晨发布新的Windows 11内部预览版系统&#xff0c;版本号为25231.1000&#xff0c;该系统对平板任务栏体验进行了改进&#xff0c;修复了系统托盘、设置等问题。下面一起来看看完整的更新内容。 更新日志 TL&#xff1b;速度三角形定位法&#xff08;dead reckoning…

【ASM】字节码操作 转换已有的类 清空方法体

1.概述 在文章:【ASM】字节码操作 转换已有的类 移除Instruction 移除NOP 中我们学会了如何移除NOP。 本章我们将学习如何清空方法体。 1.1 如何清空方法体 在有些情况下,我们可能想清空整个方法体的内容,那该怎么做呢?其实,有两个思路。 ●第一种思路,就是将instructi…

Spring中事务的传播机制以及REQUIRED、REQUIRES_NEW、NESTED区别以及代码演示

​&#x1f4d2;个人主页&#xff1a;热爱生活的李&#x1f4d2; ​❤️感谢大家阅读本文&#xff0c;同时欢迎访问本人主页查看更多文章​❤️ &#x1f64f;本人也在学习阶段&#xff0c;如若发现问题&#xff0c;请告知&#xff0c;非常感谢&#x1f64f; 事务隔离级别demo理…

[计算机网络]第一章 概述 -- 1.1 计算机网络在信息时代中的作用 1.2 互联网概述

文章目录1.1 计算机网络在信息时代中的作用1.2 互联网概述1.2.1 网络的网络1.2.2 互联网基础结构发展的三个阶段第一阶段第二阶段第三阶段1.2.3 互联网标准化工作1.1 计算机网络在信息时代中的作用 21世纪是以网络为核心的信息时代&#xff0c;21世纪的重要重要特征&#xff1a…

小侃设计模式(二)-单例模式

1.概述 设计模式在粒度和抽象层次上各不相同&#xff0c;因此从不同的角度&#xff0c;分类形式也不同&#xff0c;目前存在两种较为经典的划分方式&#xff0c;即根据模式作用的范围、模式的目的来划分。根据模式主要是用于类还是用于对象&#xff0c;可将其划分为类模式和对…

【JavaWeb】Tomcat

1.JavaWeb是指所有通过java语言编写可以通过浏览器访问的程序的总称 请求是指客户端给服务器发送数据 响应是指服务器给客户端回传数据 2.Web资源按实现的技术和呈现的效果的不同,又分为静态资源和动态资源两种. 静态资源:html css js txt mp4视频 jpg图片 动态资源:jsp页面 se…

前端工程化基建探索:从内部机制和核心原理了解npm

大厂技术 坚持周更 精选好文 前言 本文【前端工程化基建探索】的第2篇&#xff0c;上一篇 前端工程化基建探索&#xff08;1&#xff09;前端大佬&#xff0c;你好&#xff01; 当我们拉取一个前端工程化项目&#xff0c;都会通过npm/Yarn/pnpm 管理工具来安装项目的依赖&am…

大学解惑06 - 要求输入框内只能输入2位以内小数,怎么做?

请听题&#xff1a;有一个输入框&#xff0c;准备用于计算使用&#xff0c;要求点击“校验”按钮的时候进行验证&#xff0c;必须输入数字&#xff0c;并且只能是2位以内的小数&#xff0c;如果输入不合法&#xff0c;请给出提示&#xff0c;如果输入合法通过验证&#xff0c;则…

又是一篇教你摸鱼的文章,用Python实现自动发送周报给老板

前言 有没有哪个同志跟我一样&#xff0c;每周都要写工作周报 像我这种记性不好的&#xff0c;一个月四周忘记三次 索性就用Python写个小工具&#xff0c;让它每周帮我给老板发周报~ Github: Weekday 小工具 提出目标 源码.资料.素材.点击领取即可 想有一个工具能发邮件 目…

ARM 汇编基础

一、ARM架构 ARM芯片属于精简指令集计算机(RISC&#xff1a;Reduced Instruction Set Computing)&#xff0c;它所用的指令比较简单&#xff0c;有如下特点&#xff1a; 对内存只有读、写指令对于数据的运算是在CPU内部实现使用RISC指令的CPU复杂度小一点&#xff0c;易于设计…

WebShell箱子简介与原理

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是WebShell箱子简介与原理。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未授…

【面试】【项目】谷粒商城

视频链接 项目调查 这项目是这么多免费视频里最好的了。但依旧是demo。 最好还是买慕课网上的实战课&#xff0c;那些老师都有丰富的开发经验&#xff0c;有经验跟没经验讲的课是完全不同的。 谷粒商城我觉得是不low的&#xff0c;我很菜&#xff0c;毕业一年半被多次辞退&a…

数据结构之-【排序】

目录 排序 ⚡️冒泡排序 ⚡️选择排序 ⚡️插入排序 ⚡️堆排序 ⚡️归并排序 ⚡️快速排序 &#x1f3f3;️‍&#x1f308;排序 将数字从小到大的顺序排列 &#x1f534;冒泡排序 「冒泡排序」重复"从序列右边开始比较相邻两个数字的大小&#xff0c;再根据结果交换两个…