第五十九章 线段树(二)

news2024/10/6 6:50:03

第五十九章 线段树(二)

  • 一、懒标记(lazy_tag)
    • 1、作用
    • 2、思路
  • 二、结构体定义
  • 三、带有懒标记的函数操作
    • 1、pushup函数
    • 2、build函数
    • 3、modify函数
    • 4、pushdown函数
    • 5、query函数
  • 四、代码

一、懒标记(lazy_tag)

1、作用

通过第五十八章节的讲解,我们发现在上一章节中实现的线段树操作只能实现单点修改。如果是题目中要求的是区间修改操作,我们可以对该区间中的每个点进行单点修改,该思路的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的,但是如果我们想要通过 O ( l o g n ) O(logn) O(logn)的时间复杂度去实现区间修改的话,就必须使用今天所讲解的懒标记。

2、思路

懒标记的思路源于我们的查询操作。我们先回顾一下线段树的区间查询操作,查询函数的复杂度是 O ( l o g n ) O(logn) O(logn)的,他的思想就是用尽量少的子区间拼出当前区间
如下图所示:
在这里插入图片描述
我们就可以借用这个思路去实现区间修改,假设我们现在想给红色区间内的每一个数字都加上一个数,那么我们只需要找到拼出该红色区间的蓝色区间,给每个蓝色区间都打上一个标记。这个标记的含义是:给当前区间的所有子区间都加上一个数 (不包含当前区间)

二、结构体定义

struct Node
{
	int l, r;
	int sum;
	int tag;//懒标记
}tre[N * 4];

三、带有懒标记的函数操作

我们现在就以给整个区间加上一个数字为例。

1、pushup函数

这个函数我们很熟悉了,这个函数的作用就是利用子节点去更新当前节点的和。

void pushup(int u)
{
	tre[u].sum = tre[u << 1].sum + tre[u << 1 | 1].sum;
}

2、build函数

这个函数的作用就是去利用最初的原始数据建立其我们的线段树。递归建好线段树以后,我们还需要利用子区间的和维护出当前区间的信息,即在最后加上一个 p u s h u p pushup pushup操作。同时,由于我们一开始并没有给任何区间加上数字,所以我们的懒标记初始化为0即可。

void build(int u, int l, int r)
{
	if(l == r)
		tre[u] = {l, r, a[l], 0};
	else
	{
		tre[u] = {l, r};
		int mid = tre[u].l + tre[u].r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}

3、modify函数

这个函数的作用就是给一个区间加上一个数字。那么如何实现呢?我们看下面的图:
在这里插入图片描述
这个红色覆盖的区间可以分为两类。一类是完全被覆盖的区间,比如蓝色区间,那么对于这些蓝色区间而言直接加上一个懒标记即可。但是由于我们的懒标记的定义是给当前节点的子节点都加上一个数,并不包括当前节点,所以对于当前节点所维护的区间和而言,我们需要手动添加。

另外一类区间即只有部分被红色区间覆盖。那么这些区间应该怎么办呢?

由于我们的懒标记是针对整个区间而言的,所以这些部分被覆盖的区间是不能去加懒标记的。我们只能把部分覆盖转化为完全覆盖。即将当前父节点的懒标记传递给子节点,并且清空当前父节点的懒标记。直到懒标记所在的区间被红色区间完全覆盖。而这个懒标记下放的操作叫做 p u s h d o w n pushdown pushdown

p u s h d o w n pushdown pushdown函数具体怎么实现我们放到后面讲。当我们修改以后,我们还需要利用 p u s h u p pushup pushup操作更新当前节点的数值。

void modify(int u, int l, int r, int d)
{
	if(tre[u].l >= l && tre[u].r <= r)
	{
		tre[u].sum += (tre[u].r - tre[u].l + 1) * d;
		tre[u].tag += d;
	}
	else
	{
		pushdown(u);//懒标记下放
		int mid = tre[u].l + tre[u].r >> 1;
		if(l <= mid)
			modify(u << 1, l, r, d);
		if(r > mid)
			modify(u << 1 | 1, l, r, d);
		pushup(u);
	}
}

4、pushdown函数

我们再回顾一下懒标记的作用:给当前区间的子区间都加上一个数(不包括当前区间)。

同时,根据我们刚刚的 m o d i f y modify modify函数可知,我们的 p u s h d o w n pushdown pushdown函数的作用是将当前区间的懒标记传递给子节点。并且清空父节点的懒标记。为什么需要清空呢?因为如果不清空的话,对于当前区间的儿子的儿子而言,由于其父亲和父亲的父亲都有懒标记,所以这个区间会被重复加上一个数字,因此我们需要清空。

现在,我们需要根据刚刚的懒标记的作用来实现 p u s h d o w n pushdown pushdown函数。
我们将标记下传的同时,需要给左右子节点维护的区间和都加上父节点的懒标记所记录的数值。同时,还需给左右子节点维护的懒标记加上该数值。左右子节点的懒标记加上该数值很好理解,但是为什么左右子节点的区间和也要加上这个数值呢?

因为我们的懒标记是说给子树加上一个数值,而不是当前区间,也就是说如果我们只把懒标记向下传递的话,我们的左右子节点所代表的区间和是不会加上该数值的。所以,我们需要进行该操作。

void pushdown(int u)
{
	auto &root = tre[u], &left = tre[u << 1], &right = tre[u << 1 | 1];
	if(root.tag)
	{
		left.sum += (left.r - left.l + 1) * root.tag;
		left.tag += root.tag;
		right.sum += (right.r - right.l + 1) * root.tag;
		right.tag += root.tag;
		root.tag = 0;
	}
}

5、query函数

query函数的作用是查询某个区间的区间和。我们 q u e r y query query函数的思想是用尽可能少的区间去拼凑出当前查询的区间。那么在查询的过程中,如果某个区间只是部分被覆盖了,按照我们的逻辑,我们需要通过判断去递归查询该区间的子区间,但是此时我们就会面对一个问题。

这个问题就是:我们的懒标记只会给父节点标记,子节点上是没有标记的,假设我们讨论的这个部分覆盖的区间上是有懒标记的,我们查询到他的子区间的时候,子区间是没有标记的,但实际上这个区间是应该被加上一个数字的。

因此,为了解决这个问题,我们只需要在向下查询的时候,加上我们的 p u s h d o w n pushdown pushdown操作。

int query(int u, int l, int r)
{
	if(tre[u].l >= l && tre[u].r <= r)
		return tre[u].sum;
	
	pushdown(u);
	int res = 0;
	int mid = tre[u].l + tre[u].r >> 1;
	if(l <= mid)
		res += query(u << 1, l, r);
	if(r > mid)
		res += query(u << 1 | 1, l, r);
	return res;
}

四、代码

#include<bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 10;
int n, m;
int a[N];
struct Node
{
	int l, r;
	int sum;
	int tag;
}tre[N * 4];

void pushup(int u)
{
	tre[u].sum = tre[u << 1].sum + tre[u << 1 | 1].sum;
}

void pushdown(int u)
{
	auto &root = tre[u], &left = tre[u << 1], &right = tre[u << 1 | 1];
	if(root.tag)
	{
		left.sum += (left.r - left.l + 1) * root.tag;
		left.tag += root.tag;
		right.sum += (right.r - right.l + 1) * root.tag;
		right.tag += root.tag;
		root.tag = 0;
	}
}

void build(int u, int l, int r)
{
	if(l == r)
		tre[u] = {l, r, a[l], 0};
	else
	{
		tre[u] = {l, r};
		int mid = tre[u].l + tre[u].r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}

void modify(int u, int l, int r, int d)
{
	if(tre[u].l >= l && tre[u].r <= r)
	{
		tre[u].sum += (tre[u].r - tre[u].l + 1) * d;
		tre[u].tag += d;
	}
	else
	{
		pushdown(u);
		int mid = tre[u].l + tre[u].r >> 1;
		if(l <= mid)
			modify(u << 1, l, r, d);
		if(r > mid)
			modify(u << 1 | 1, l, r, d);
		pushup(u);
	}
}

int query(int u, int l, int r)
{
	if(tre[u].l >= l && tre[u].r <= r)
		return tre[u].sum;
	
	pushdown(u);
	int res = 0;
	int mid = tre[u].l + tre[u].r >> 1;
	if(l <= mid)
		res += query(u << 1, l, r);
	if(r > mid)
		res += query(u << 1 | 1, l, r);
	return res;
}

void solve()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ )
		cin >> a[i];
	build(1, 1, n);
	while(m--)
	{
		char opt;
		int l, r, d;
		cin >> opt >> l >> r;
		if(opt == 'C')
		{
			cin >> d;
			modify(1, l, r, d);
		}
		else
			cout << query(1, l, r) << endl;
	}
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	solve();
}

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

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

相关文章

redis五大基本数据类型之(源码分析)

redis五大数据结构StringHashsetListZset总结String String 是最基本的 key-value 结构&#xff0c;key 是唯一标识&#xff0c;value 是具体的值&#xff0c;value其实不仅是字符串&#xff0c; 也可以是数字&#xff08;整数或浮点数&#xff09;&#xff0c;value 最多可以…

远程桌面连接是什么?远程桌面连接使用教程

有时候电脑出现各类网络连接、网址访问出错问题&#xff0c;自己无法解决的情况下&#xff0c;常常会求助其他擅长IT的同事或朋友&#xff0c;要么自己通过社交工具在线沟通、要么抱着电脑找人家解决。然而&#xff0c;通过远程桌面完全可以让朋友同事远程帮自己查看电脑问题&a…

永远加载不满的进度条

前言 各位开发大佬&#xff0c;平时肯定见到过这种进度条吧&#xff0c;一直在加载&#xff0c;但等了好久都是在99% 如下所示&#xff1a;有没有好奇这个玩意儿咋做的呢&#xff1f;细听分说 &#xff08;需要看使用&#xff1a;直接看实践即可&#xff09; fake-progress …

亚马逊云科技Serverless Data:数字经济下的创新动能

Serverless时代已经到来&#xff01;企业的技术架构&#xff0c;总是伴随着不断增长的数据与日趋复杂的业务持续演进。如何通过构建更易用的技术架构来聚焦在业务本身&#xff0c;而不必在底层基础设施的管理上投入过多的精力&#xff0c;是数据驱动型企业需要思考的重要议题。…

实操| 前端新人无敲代码开发APP

作为一种大型的基于GPT-3. 5结构的语言模型&#xff0c;ChatGPT由OpenAI训练&#xff0c;采用深度学习技术&#xff0c;通过大量的文本数据学习&#xff0c;可以生成类似于人类自然语言的文字。ChatGPT是一种非常强大的对话引擎&#xff0c;能进行对话、回答问题和完成任务。Ch…

数据库锁原理

数据库锁原理锁的定义InnoDB中的锁模式共享锁独占锁共享意向锁和独占意向锁LOCK_AUTO_INC自增锁INNODB_AUTOINC_LOCK_MODEInnoDB中的锁类型表锁行锁行子类型LOCK_REC_NOT_GAP精准行锁LOCK_GAP行GAP锁LOCK_ORDINARY行NEXT-KEY锁LOCK_INSERT_INTENTION插入意向锁锁的定义 为了体…

数据分析之Matplotlib 基础入门

目录 第一章 什么是Matplotlib 常见图表及其分类 Matplotlib 第一个绘图程序 第二章 Matplotlib 基础 Matplotlib 图表常用设置 颜色设置 线条样式和标记样式 画布设置 设置坐标轴标题 设置坐标轴刻度 设置坐标轴范围 设置网格线 设置文本标签和标题 添加图例 添…

Image Deconvolution with the Half-quadratic Splitting Method

Image Deconvolution with the Half-quadratic Splitting Method 在处理图像重建或者逆问题的时候&#xff0c;我们经常会看到一种称为 Half-quadratic Splitting&#xff08;HQS&#xff09;的方法&#xff0c;这是在优化领域里非常经典的一种方法&#xff0c;之前也断断续续…

【Cesium 编程第一篇】概述、环境搭建、界面介绍

年前年后一直在面试&#xff0c;发现一个奇怪的现象&#xff1a;很多互联网公司经受住三年的疫情冲击&#xff0c;反而在疫情放开的那一刻撑不住了&#xff0c;很多大厂都在批量的裁员&#xff1a;美国硅谷、北京字节、迪士尼中国等等。在北京的朋友也是年后到现在一直没有找到…

AI是一场革命,我真不是在跟风

AI是场革命&#xff0c;好像现在很多人都开始这么说&#xff0c;那么我说我不是在跟风&#xff0c;为什么&#xff1f;不好意思&#xff0c;又要翻翻旧贴 -> AI是一场革命&#xff0c;不要笑&#xff0c;我是认真的。2016年我就这样讲了&#xff0c;就如我常说的&#xff0c…

【《中国工业经济》论文复刻】“一带一路”倡议与中国企业升级

数据和变量描述 本部分介绍文章研究所使用的数据和关键变量。 数据来源&#xff1a;自主整理 时间范围&#xff1a;2012-2017年 变量说明&#xff1a; 相关变量见下表。 一. 摘要 近年来&#xff0c;中国应该如何实现产业升级受到学界的广泛关注&#xff0c;产业升级归根…

Widows下安装Nginx并设置开机自启

1 下载Nginx 下载地址&#xff1a;http://nginx.org/en/download.html 2 启动Nginx nginx的启动方式有两种&#xff1a;一种是直接点击nginx.exe启动&#xff0c;另一种是通过命令行启动 2.1 直接启动 找到nginx目录&#xff0c;双击nginx.exe 即可启动 2.2 命令行启动…

不卷不成魔,新时代的IT人员更需要卷,不卷不成活

简介 从2022年开始至今&#xff0c;IT界发生了很多巨大的变革带来了许多巨大的变化。 这些变革、这些变化导致了有人欢喜有人悲、有人迷茫有人焦虑。1年半来&#xff0c;迷茫、焦虑、精神内耗了也都差不多了&#xff0c;大家都已经认识到了现实&#xff0c;作为凡人的我们所能…

Moviepy模块之多图拼接为一个动图

文章目录前言项目场景项目素材1.jpg2.jpg3.jpg项目代码1. 引入库2. 读取存储图片的文件夹3. 获取文件夹中所有的.jpg结尾的图片文件名4. 按照文件名排序5. 读取所有图片并拼接成动图6. 保存动图问题描述原因分析解决方案最终效果前言 大家好&#xff0c;我是空空star&#xff0…

《花雕学AI》16:BingGPT桌面端的另外一个惊喜—完美整合了新Bing的AI作画功能

你是否曾经想过&#xff0c;如果你能用语言描述你想要的画面&#xff0c;就能让AI为你生成一幅美丽的图画&#xff0c;那该有多好&#xff1f;你是否曾经想过&#xff0c;如果你能在桌面端直接与新Bing进行智能、流畅、有趣的对话&#xff0c;而不需要打开浏览器或安装插件&…

好看的html登录界面,

界面效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><title>Login Page</title><style>body {background-color: #f2f2f2;font-family: Arial, sans-serif;}form {background-color: #fff;border-radius: 5px;box-shado…

【LeetCode: 剑指 Offer II 085. 生成匹配的括号 | 递归 | 回溯】

&#x1f34e;作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域新星创作者&#x1f3c6;&#xff0c;保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享&#x1f48e;&#x1f48e;&#x1f48e; &#x1f34e;座右…

文本翻译免费软件-word免费翻译软件

好用的翻译文件软件应该具备以下几个方面的特点&#xff1a;支持多种文件格式&#xff0c;翻译结果准确可靠&#xff0c;界面操作简便易用&#xff0c;价格实惠&#xff0c;用户体验舒适。以下是几个好用的翻译文件软件&#xff1a; 1.147cgpt翻译软件 翻译软件特点&#xff1…

Diffusion Models 简单代码示例

一、关于Diffusion 模型的简单介绍 首先diffusion模型和VAE、Flow、Gan等模型类似&#xff0c;均属于生成模型&#xff0c;可以和GCN、CNN等其他深度学习网络相结合&#xff0c;完成特定的生成任务&#xff0c;如下图&#xff1a; 基于 GAN 生成模型&#xff0c;基于 VAE 的生成…

初识二叉树

树的概念与结构 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合&#xff0c;把它叫做树是因为它看起来像一颗倒挂的树&#xff0c;也就是说它的根朝上&#xff0c;而叶朝下的。 子树之间不能有交集&#xff0c;否则就不是树型结构 比如下面…