(模板)线段树(区间双重更新,区间求和)

学习笔记·对于线段树双重标记的理解 在开始这个话题之前,先简略说明一下单个lazy标记的作用与意义(想详尽了解的话自行百度一下,有很多)。 lazy标记 作用:降低区间操作的复杂度 意义:lazy标记表示已对当前节点进行了相应修改,而暂时没有对其左右子节点进行修改 注意:一旦打了标记就要修改当前节点的值,标记只表明未修改其左右子节点,而非未修改当前节点。 双重标记 一些题有多种区间修改方式,仅用一个标记并不能很好... 阅读详情

题目可参见洛谷P3373
已知一个数列,你需要进行下面三种操作:

1.将某区间每一个数乘上x

2.将某区间每一个数加上x

3.求出某区间每一个数的和

首先先说明本题的思路。题目要求有三种操作,两种是不同的在线修改,还有一种是在查询取模后的结果。而这两种操作又是区间乘法和区间加法,我们可以惊喜的发现这两种操作对于取模运算来说都是自由的!但是面对非常大的数据,我们必须思考怎么样用线段树优雅的跑过这道题目。

面对这两种操作,可以联想到线段树的一个非常好的功能就是lazytag,只计算出确实需要访问的区间的真实值,其他的保存在lazytag里面,这样可以近似O(NlogN)的运行起来。在尝试着写了只有一个lazetag的程序之后我们发现一个lazytag是不能够解决问题的,那就上两个,分别表示乘法意义上的lazytag和加法意义上的lazytag。紧接着想到pushdown操作之后我们又发现必须在向下传递lazytag的时候人为地为这两个lazytag规定一个先后顺序,排列组合一下只有两种情况:

①加法优先,即规定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,问题是这样的话非常不容易进行更新操作,假如改变一下add的数值,mul也要联动变成奇奇怪怪的分数小数损失精度,我们内心是很拒绝的;

②乘法优先,即规定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本区间长度))%p,这样的话假如改变add的数值就只改变add,改变mul的时候把add也对应的乘一下就可以了,没有精度损失,看起来很不错。
一定要注意顺序的问题!!
代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int array1[maxn], n, m, p;
struct node1 {//sum是区间值,add是加的记号,另一个是乘
    ll sum, add, mul;
}Node[maxn<<2];
void pushup(int node)
{
    Node[node].sum = (Node[node << 1].sum + Node[node << 1 | 1].sum)%p;
}
void buildtree(int node, int begin, int end)
{
    Node[node].add = 0; Node[node].mul = 1;
    if (begin == end) {
        Node[node].sum = array1[begin];
        return;
    }
    int m = (begin + end) >> 1;
    buildtree(node * 2, begin, m);
    buildtree(node * 2 + 1, m + 1, end);
    pushup(node);
}
void pushdown(int node,int process, int begin, int end)
{
    if (process == 1) {
        if (Node[node].mul != 1) {//process是1代表乘,是0代表加(下同)
            Node[node << 1].mul = Node[node].mul*Node[node << 1].mul%p;
            Node[node << 1 | 1].mul = Node[node].mul*Node[node << 1 | 1].mul%p;
            Node[node << 1].add = Node[node].mul*Node[node << 1].add%p;
            Node[node << 1 | 1].add = Node[node].mul*Node[node << 1 | 1].add%p;
            Node[node << 1].sum = Node[node].mul*Node[node << 1].sum%p;
            Node[node << 1 | 1].sum = Node[node].mul*Node[node << 1 | 1].sum%p;
            Node[node].mul = 1;
        }
    }
    else {
        if (Node[node].add) {
            int m = (begin + end) >> 1;
            Node[node << 1].add = (Node[node].add + Node[node << 1].add) % p;
            Node[node << 1 | 1].add = (Node[node << 1 | 1].add + Node[node].add) % p;
            Node[node << 1].sum = (Node[node << 1].sum + (m - begin + 1)*Node[node].add%p) % p;
            Node[node << 1 | 1].sum = (Node[node << 1 | 1].sum + (end - m)*Node[node].add%p) % p;
            Node[node].add = 0;
        }
    }
}
ll query(int node, int begin, int end, int left, int right)
{
    if (left <= begin&&right >= end) {
        return Node[node].sum%p;
    }
    pushdown(node, 1, begin, end);//(先乘后加)
    pushdown(node, 0, begin, end);
    ll ans = 0;
    int m = (begin + end) >> 1;
    if (left <= m)
        ans = (ans+query(node * 2, begin, m, left, right))%p;
    if (right > m)
        ans = (ans + query(node * 2 + 1, m + 1, end, left, right))%p;
    return ans;
}
void update(int node, int process, int add, int begin, int end, int left, int right)
{
    if (left <= begin&&right >= end) {
        if (process == 1) {
            Node[node].mul = Node[node].mul*add%p;
            Node[node].add = Node[node].add*add%p;
            Node[node].sum = Node[node].sum*add%p;
        }
        else {
            Node[node].sum = (Node[node].sum + add*(end-begin+1)%p) % p;
            Node[node].add = (Node[node].add + add) % p;

        }
        return;
    }
    int m = (begin + end) >> 1;
    pushdown(node, 1, begin, end);
    pushdown(node, 0, begin, end);
    if (left <= m)
        update(node * 2, process, add, begin, m, left, right);
    if (right > m)
        update(node * 2 + 1, process, add, m + 1, end, left, right);
    pushup(node);
}
int main(void)
{
    cin >> n >> m >> p;

    for (int i = 1; i <= n; i++) {
        scanf("%d", &array1[i]);
    }
    buildtree(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int a, b, c, d;
        scanf("%d", &a);
        if (a == 1) {
            scanf("%d %d %d", &b, &c, &d);
            update(1, 1, d, 1, n, b, c);
        }
        else if (a == 2) {
            scanf("%d %d %d", &b, &c, &d);
            update(1, 0, d, 1, n, b, c);
        }
        else {
            scanf("%d %d", &b, &c);
            printf("%lld\n", query(1, 1, n, b, c));
        }
    }

    return 0;
}
SimNow模拟环境实战:用vnpy_ctp连接期货市场的5个关键配置步骤 本文提供了一份详细的量化交易实战指南,重点讲解如何使用vnpy_ctp插件连接SimNow期货模拟环境的5个关键配置步骤。内容涵盖SimNow环境选择、vnpy_ctp核心参数解析、实战连接脚本编写、深度排错技巧以及向实盘迁移的准备,旨在帮助量化交易开发者快速搭建可靠的测试环境,避免常见陷阱。 阅读详情

相关推荐

LTE网络下的SMS收发(SMS Over SGs方案的详细流程)

关键词: IMS,SMS, LTE, MSC, MME, EPS,eNodeB, SMS Over SGs, SMS Over IMS 2G时代由于采用了比较复杂的CS/PS域架构,所以SMS(短信)作为上个世纪的产物,一直寄居在2G的CS域架构下。看看核心网的配置,其实SMS的收发还是比较复杂的。 介绍下面的信息之前你需要去百度一下这些关键词,这里我只说明比较重要的三个单元 MS 移动终端...

kevin的博客 2万+

线段树区间操作

Codeforces 483D要求单点修改,区间求和,区间取模 由于一个数只能被取模log次,所以区间取模就变成了单点修改,同时对区间维护一个mx值,如果mx小于模数就不取模#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=100000+10; int setv[maxn*4],mx[m

MatouKariya的专栏 791

告别Keil手动分区!用CubeMX+VS Code一键生成带Bootloader的STM32 OTA工程

本文详细介绍了如何利用STM32CubeMX和VS Code实现STM32 OTA工程的自动化构建,告别传统Keil手动分区的繁琐流程。通过图形化配置Flash分区、自动生成链接脚本以及实现Bootloader跳转逻辑,开发者可以高效完成远程升级功能开发,显著提升嵌入式开发效率。

weixin_33720956的博客 389

线段树--区间乘法和区间加法

A Simple Problem with Integers 区间查询,区间修改 //#include<bits/stdc++.h> #include<iostream> using namespace std; template <typename T> void debug(string s, T x) { cout << s << "=" << x << "\n"; } typedef long long ll; ty

weach的博客 2431

嵌入式音频架构 - AudioWeaver 整体概念

AudioWeaver 是DSP Concept公司推出的基于嵌入式平台的音频框架。由AWE Core embedded processing engine 和 Audio Weaver Designer IDE 组成。 Audio Weaver Designer 有三个版本,ST Edition, Standard Edtion 和 Pro Edtion。 Audio Weaver Designer界面: Audio Weaver 的整体使用架构: AWD算法流文件,AWS配置脚本文件,AWB嵌入式b

甲客实验室 6874

目标检测数据集 第069期-基于yolo标注格式的玻璃清洁状态检测数据集(含免费分享)

但这类技术的落地,离不开大量标注规范、场景丰富的数据集作为算法训练的支撑,玻璃清洁状态检测数据集的出现,正是为了满足这一市场与技术需求。在高校与科研机构的计算机视觉研究中,该数据集可作为基础研究素材,用于探索物体状态识别的算法优化方向,比如针对玻璃这类透明物体的检测算法改进,或是小样本场景下的状态识别研究。通过摄像头采集车窗、挡风玻璃的图像,算法自动检测玻璃清洁度,若识别到脏污影响视线,及时提醒运维人员清洁,避免因玻璃脏污引发的安全事故,提升交通出行的安全性。

深度学习算法工程师,专注于前沿算法研究与实战应用。在这里,我将分享深度学习领域的实战经验、优质数据集资源以及最新论文解读与精度分析,旨在与广大技术爱好者共同探索人工智能的奥秘,助力技术成长与创新。 128

【高级数据结构】线段树 | 求区间和

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 用于数组的区间求和,且数组内的元素可能改变。 对于数组 arr = {1,2,3,4,5,6,6,7,…} 而言,要求得 i~j 下标数据的和,我们需要对数组进行遍历。时间复杂度O(n)。而修改数组中某个元素的值,时间复杂度为O(1)。 我们可以使用一个 sum_arr 前缀和数组来保存 0~i 位置的和,这样以来,求 i~j 的区间和等同于 arr[j] - arr[i-1]。 时间复杂度为 O(

专注于C/C++/Linux领域创作 942

你以为你懂 Java 反射?看了这篇源码分析,我发现我以前都错了

摘要:本文讲述了一次生产环境Metaspace内存溢出的故障排查经历。事故源于一个通用数据导出功能中频繁使用反射调用method.invoke(),触发了JVM的"膨胀机制":当同一Method对象调用超过15次时,JVM会动态生成GeneratedMethodAccessor类,导致Metaspace被撑爆。文章分析了问题根源在于循环中反复调用getMethods()创建新Method对象,并提出了三种解决方案:缓存Method对象、改用MapStruct/MethodHandle工具

BUG猿的博客 769

线段树(以区间和为例)

​ 线段树的根节点是一个长区间,例如输入不超过N个的数,那么可以将根节点设为[1,N],它的左右子节点为均分父节点的两个区间,即左半区间和右半区间1⌊N12⌋⌊N12⌋1N1⌊2N1​⌋][⌊2N1​⌋1N,依次递归地建立,直到叶子节点,即区间ii[i,i]ii。

shiki217_的博客 1694

Python之aaindex包语法、参数和实际应用案例

`aaindex`是一个专为生物信息学设计的Python包,主要用于处理氨基酸指数(AAindex)数据库。该数据库收集了各种氨基酸的物理化学性质和统计特性,涵盖疏水性、电荷、体积等500多种特性。`aaindex`包提供了便捷的接口,让用户能够快速访问这些数据,并应用于蛋白质序列分析、结构预测、功能推断等领域。

王国平的博客 1036

半车(前后、左右)、整车悬架详细推导建模和simulink仿真分析

用达朗贝尔定理依据悬架数学模型,得到悬架模型微分方程,分别用simulink建立前后半车、左右半车悬架;结合转向2自由度模型与半车侧倾模型,对模型考虑相干性、延时性的C级随机路面下,仿真分析给定角阶跃输入下的侧倾稳定性。

Mr.邹的博客 1万+

高级数据结构之线段树,区间求和,最大值最小值

高级数据结构,线段树,区间操作

haruakagasukida的博客 875

ESP32-C3入门教程 环境篇②——Windows开发环境准备

文章目录一、注意事项二、下载ESP-IDF工具安装器三、安装内容四、详细安装步骤报错处理 一、注意事项 请注意 ESP-IDF 和 ESP-IDF 工具的安装路径不能超过 90 个字符,安装路径过长可能会导致构建失败。 Python 或 ESP-IDF 的安装路径中一定不能包含空格或括号。 除非操作系统配置为支持 Unicode UTF-8,否则 Python 或 ESP-IDF 的安装路径中也不能包括特殊字符(非 ASCII 码字符、中文字符) 二、下载ESP-IDF工具安装器 ESP-IDF Win

5242

P3373 【模板】线段树 2

题目P3373要求实现一个支持区间乘法、区间加法和区间求和的线段树。给定初始数列和一系列操作,包括将区间内每个数乘/加一个值,以及查询区间和对模数取模的结果。使用双懒标记(乘法和加法)来高效处理区间更新,保证在O(log n)时间内完成操作。代码实现了建树、区间乘法、区间加法和查询功能,通过维护两个懒标记来优化性能,适用于大规模数据(n,q≤1e5)。样例输入输出验证了正确性。

Lsq_MAX_4869的博客 540

NTscan扫描工具

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

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

树状数组从原理到实战:单点修改、区间求和与逆序对高效解法

在算法与数据结构中,动态维护数组的区间和是一类高频需求,而树状数组(Fenwick Tree)正是平衡效率与实现复杂度的重要工具。它基于 lowbit 运算构建部分和结构,支持 O(logn) 的单点修改与区间查询,相比前缀和要求数据静态、线段树则代码冗长,树状数组在特定场景下更轻快实用。掌握其原理后,还能扩展到树状数组上二分查找第 k 小元素,以及通过离散化处理大值域数据,高效统计逆序对数量。这些技巧广泛应用于竞赛编程、搜索引擎、实时排名系统等工程场景。本文从基础概念出发,结合手写实现与常见错误排查,帮助

weixin_33768481的博客 345

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Check Corners 【HDU - 2888】【二维线段树】

题目链接   很多人写这道题都用的是二维RMQ,但是,我觉得这道题可以锻炼一下我二维线段树的思维,但是,无独有偶,这道题会卡一些二维线段树的模板,一开始我想也没想,直接敲了刚学的线段树,然后不停的RE,后来改了下,换成单点更新与区间更新二维线段树,还是不行TLE了,于是,就开始想,我们该如何把二维线段树双重区间写出来,然后,研究了下他的向下更新操作,我们不妨可以结合一下单点更新作出改进,譬如看向...

既然弱小,就只顾变强就是了 275

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

树状数组原理与实战:从lowbit到区间更新、第k小与逆序对

在算法与数据结构中,前缀和与单点修改是两类高频操作,但朴素实现往往顾此失彼。树状数组凭借极简的代码和O(log n)的稳定复杂度,成为平衡修改与查询的经典方案。其核心源于lowbit运算,通过巧妙的区间覆盖设计,将单点增加和前缀求和都转化为二进制位上的跳跃。进一步结合差分思想,树状数组可以扩展为区间更新与区间查询的利器;利用权值数组与树状数组上二分,还能高效解决动态第k小问题;配合离散化,它也是逆序对计数、频次统计等场景中的常用工具。相比线段树,树状数组代码更短、常数更小,更适合求和与计数类问题。本文从lo

weixin_34293246的博客 405

[英雄星球六月集训LeetCode解题日报] 第24日 线段树

线段树2题

liuliangcan的博客 335

简单子串与区间问题核心解题思路:前缀哈希 & 双指针(例题 + 通用模板 + 现实场景)

在算法刷题中,连续子串、连续子数组、区间统计是出现频率最高的题型。绝大多数人刚开始做题的思维都是:暴力双重循环枚举所有区间[l, r],时间复杂度O(n²)。一旦数据量变大,暴力必超时。其实绝大多数子串和区间问题,根本不需要暴力枚举前缀预处理 + 哈希映射双指针滑动窗口本文通过两道经典例题带入思路,再提炼通用解题模板,最后结合现实场景理解算法意义,彻底学透区间类问题。拒绝重复计算,复用已经推导过的信息。无前缀单调性、需要匹配历史状态→ 前缀哈希有窗口单调性、流式推进→ 双指针滑动窗口。

2502_93562147的博客 607

从洛谷P5744题解看顺序模拟与区间更新算法的核心思路与实践

在算法与数据结构的学习中,顺序模拟和区间更新是两类基础且重要的操作范式。顺序模拟要求我们严格按照操作指令的先后顺序执行,每一步的结果都可能影响后续状态,这体现了计算机程序执行的本质。区间更新则涉及对数据集中连续一段元素的统一修改,其高效实现是算法优化的关键。结合线段树等数据结构,可以大幅提升区间查询与更新的效率,这对于处理大规模数据、构建高性能系统具有重要价值。本文以一道典型的编程竞赛题目(洛谷P5744)为切入点,深入剖析了顺序模拟与区间更新相结合的应用场景。通过详细讲解暴力解法的实现细节、测试用例设计以

weixin_30522983的博客 227

差分算法详解:从一维到二维,高效解决区间批量修改问题

在算法和数据处理中,前缀和是一种基础且高效的预处理技术,它通过空间换时间,能在常数时间内查询任意区间和。差分算法是前缀和的逆运算,其核心原理在于将原数组视为差分数组的前缀和。这种设计带来了巨大的技术价值:它能够将原本需要线性时间复杂度的区间批量修改操作,优化为仅修改差分数组端点的常数时间操作,极大地提升了处理频繁区间更新场景的效率。差分思想在工程实践中应用广泛,例如在需要大量区间加减的模拟计算、数据流分析以及图像处理中的像素区域调整等场景下,都能发挥关键作用。本文深入解析了一维和二维差分的实现细节,并提供了

weixin_30561425的博客 555

009 ST表:静态区间最值的极致优化

ST表是。

weixin_37253733的博客 509

Poj 3468 A Simple Problem with Integers

需要lazy思想的线段树 原本都关机睡觉了,来了灵感,双重维护 听说过lazy的想法,实现方式是自己想到的,增量+子区间总和 5Y 3*TLE 1*WA 这个可以当做自己的模板了吧~~ TLE的关键点在于 insert和sum的结束条件都不能是T[w].l==T[w].r这样比O(n)的朴素算法还慢..貌似 想要加快sum,需要维护下段总和 想要加快insert,需要维护区间上

On my fighting way 568

树状数组全解析:原理、模板与逆序对实战

在算法设计与工程实践中,动态维护序列前缀和与区间查询是高频需求。树状数组(Fenwick Tree)作为一种轻量级数据结构,通过巧妙的lowbit运算,将单点修改与区间查询的复杂度均优化至O(log n)。它既不是单纯的数组,也不是显式的树,而是利用二进制索引构建的逻辑树形结构,兼顾了实现简单与性能高效。面向算法竞赛与面试场景,树状数组常被用于解决逆序对计数、动态第k小元素、差分区间修改等经典问题,并常与离散化技术结合以处理大规模数值范围。本文从基础原理出发,逐步推导核心操作,并给出多语言模板与实战代码,帮

weixin_30460489的博客 370

二维前缀和算法:从暴力求和到O(1)查询的降维打击

在数据处理和算法领域,区间求和是一个基础且高频的操作。对于一维数组,前缀和通过预处理将单次查询复杂度优化至O(1)。其核心原理是预先计算并存储从起点到每个位置的累计和,从而将后续的区间求和转化为两次数组访问的减法运算。这种‘空间换时间’的思想在二维矩阵中得到了延伸,即二维前缀和。二维前缀和通过构建一个额外的累积和矩阵,将任意矩形区域的求和计算,从暴力遍历的O(k*l)优化为仅需四次加减运算的O(1)操作,技术价值在于极大提升了涉及密集查询场景的效率。其典型应用场景包括图像处理中的积分图计算、游戏地图的资源统

weixin_33946020的博客 306

LeetCode 731. My Calendar II【设计,有序映射,差分;线段树】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。为了方便在PC上运行调试、分享代码文件,我还建立了相关的。

memcpy0的博客 513
上一篇: (模板)线段树 (区间更新,区间求和)
下一篇: (模板)线段树(单点更新,单点求值)
HumveeA6
博客等级 码龄17年 12粉丝 175原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值