线段树模板12

news2024/10/7 12:18:38

线段树

洛谷上有两道线段树模板(指模板1,模板2)都是区间维护的,也就是说,都离不开lasytag的维护,为了提高效率,故使用了lasytag,这里看一下题

【模板】线段树 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 k k k
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 n , m n, m n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

接下来 m m m 行每行包含 3 3 3 4 4 4 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 [ x , y ] [x, y] [x,y] 内每个数加上 k k k
  2. 2 x y:输出区间 [ x , y ] [x, y] [x,y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

样例 #1

样例输入 #1

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

样例输出 #1

11
8
20

提示

对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
对于 70 % 70\% 70% 的数据: n ≤ 10 3 n \le {10}^3 n103 m ≤ 10 4 m \le {10}^4 m104
对于 100 % 100\% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1n,m105

保证任意时刻数列中所有元素的绝对值之和 ≤ 10 18 \le {10}^{18} 1018

【样例解释】

分析1.1

操作很少,只有:

  • 区间加
  • 区间求和

我们用数组存储线段树每个节点的状态即可,看代码:

代码1

#include <bits/stdc++.h>
using namespace std;
const int M = 1e5+10;
#define int long long
int a[M];
struct segment {
#define lc(p) (p<<1)
#define rc(p) ((p<<1)|1)
	int tag[M << 2], sum[M << 2];
	void f(int o, int l, int r, int k) {
		tag[o] += k;
		sum[o] += k * (r - l + 1);
	}
	void push_up(int x) {
		sum[x] = sum[lc(x)] + sum[rc(x)];
	}
	void push_down(int o, int l, int r) {
		int mid = l + r >> 1;
		f(lc(o), l, mid, tag[o]);
		f(rc(o), mid + 1, r, tag[o]);
		tag[o] = 0;
	}
	void build(int o, int l, int r) {
		if (l == r) { sum[o] = a[l]; return; }
		int mid = l + r >> 1;
		build(lc(o), l, mid);
		build(rc(o), mid + 1, r);
		push_up(o);
	}
	int query(int ql, int qr, int o, int l, int r) {
		if (l>qr or r<ql) return 0;
		if (ql <= l and r <= qr) return sum[o];
		int ans = 0;
		int mid = l + r >> 1;
		push_down(o, l, r);
		ans += query(ql, qr, lc(o), l, mid);
		ans+=query(ql,qr,rc(o),mid+1,r);
		return ans;
	}
	void update(int ql,int qr,int o,int l,int r,int k){
		if (l>qr or r<ql) return;
		if(ql<=l and r<=qr) {f(o,l,r,k);return;}
		push_down(o,l,r);
		int mid = l + r >> 1;
		update(ql, qr, lc(o), l, mid,k);
		update(ql,qr,rc(o),mid+1,r,k);
		push_up(o);
	}
}t1;
signed main() {
	int n,m;
	cin >> n>>m;
	for (int i = 1; i <= n; i++) cin >> a[i];
	t1.build(1,1,n);
	for (int i=1;i<=m;i++){
		int x,y,k;
		cin>>x;
		if (x==1){
			cin>>x>>y>>k;
			t1.update(x,y,1,1,n,k);
		}
		else{
			cin>>x>>y;
			cout<<t1.query(x,y,1,1,n)<<endl;
		}
	}
	return 0;
}

分析1.2

我们只需要理解segment结构体

  • sum[i]表示线段树第i个节点对应的区间和
  • tag[i]就是节点i的懒标记

所以我们需要一个函数来标记节点,并修改此节点的和

void f(int o, int l, int r, int k) {
		tag[o] += k;
		sum[o] += k * (r - l + 1);
	}

f函数可以做到,它可以保存标记,并区间修改
当然标记需要下传,修改子节点后,父节点也要改变:

void push_up(int x) {
		sum[x] = sum[lc(x)] + sum[rc(x)];
	}

push_up可以重新求父节点的值

	void push_down(int o, int l, int r) {
		int mid = l + r >> 1;
		f(lc(o), l, mid, tag[o]);
		f(rc(o), mid + 1, r, tag[o]);
		tag[o] = 0;
	}

push_down可以下传标记
而查询与更新只要递归查询,合并答案即可

【模板】线段树 2

题目描述

如题,已知一个数列,你需要进行下面三种操作:

  • 将某区间每一个数乘上 x x x
  • 将某区间每一个数加上 x x x
  • 求出某区间每一个数的和。

输入格式

第一行包含三个整数 n , q , m n,q,m n,q,m,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

接下来 q q q 行每行包含若干个整数,表示一个操作,具体如下:

操作 1 1 1: 格式:1 x y k 含义:将区间 [ x , y ] [x,y] [x,y] 内每个数乘上 k k k

操作 2 2 2: 格式:2 x y k 含义:将区间 [ x , y ] [x,y] [x,y] 内每个数加上 k k k

操作 3 3 3: 格式:3 x y 含义:输出区间 [ x , y ] [x,y] [x,y] 内每个数的和对 m m m 取模所得的结果

输出格式

输出包含若干行整数,即为所有操作 3 3 3 的结果。

样例 #1

样例输入 #1

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

样例输出 #1

17
2

提示

【数据范围】

对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n8 q ≤ 10 q \le 10 q10
对于 70 % 70\% 70% 的数据:$n \le 10^3 , , q \le 10^4$。
对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1q105

除样例外, m = 571373 m = 571373 m=571373

(数据已经过加强 _

样例说明:

故输出应为 17 17 17 2 2 2 40   m o d   38 = 2 40 \bmod 38 = 2 40mod38=2)。

分析2

多了个乘法,再开数组代码逻辑会乱一点,但开个结构体即可:

struct node{
	int sum,mul,add;
};

不难想到,sum为区间和,mul为乘法懒标记,add为加法懒标记,对于一个区间x,可以存在子节点y,y应该接受懒标记:
y s u m = ( y s u m ∗ x m u l ) + x a d d ∗ ( y r − y l + 1 ) y_{sum}=(y_{sum}*x_{mul})+x_{add}*(y_r-y_l+1) ysum=(ysumxmul)+xadd(yryl+1)
修改y的懒标记:
y m u l = y m u l ∗ x m u l y_{mul}=y_{mul}*x_{mul} ymul=ymulxmul
y a d d = y a d d ∗ x m u l + x a d d y_{add}=y_{add}*x_{mul}+x_{add} yadd=yaddxmul+xadd

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rc(x) ((x<<1)|1)
#define lc(x) (x<<1)
const int M = 1e6;
const int N=1e18;
int a[M],n,m,mod;
void read(){
	cin>>n;cin>>m>>mod;
	for (int i=1;i<=n;i++) cin>>a[i];
}
struct node{
	int sum,mul,add;
};
struct segment{
	node seg[M<<2];
	void push_up(int x){
		seg[x].sum=seg[lc(x)].sum+seg[rc(x)].sum;
	}
	void build(int o,int l,int r){
		seg[o].mul=1;seg[o].add=0;
		if (l==r){
			seg[o].sum=a[l];
			return;
		}
		int mid=(l+r)>>1;
		build(lc(o),l,mid);
		build(rc(o),mid+1,r);
		push_up(o);
	}
	void f(int o,int l,int r,int add,int mul){
		seg[o].mul=(seg[o].mul*mul)%mod;
		seg[o].add=(seg[o].add*mul)%mod;
		seg[o].add=(seg[o].add+add)%mod;
		seg[o].sum=(seg[o].sum*mul)%mod;
		seg[o].sum=(seg[o].sum+(r-l+1)*add)%mod;
	}
	void push_down(int o,int l,int r){
		int mid=(l+r)>>1;
		f(lc(o),l,mid,seg[o].add,seg[o].mul);
		f(rc(o),mid+1,r,seg[o].add,seg[o].mul);
		seg[o].add=0;seg[o].mul=1;
	}
	void update(int ql,int qr,int add,int mul,int o=1,int l=1,int r=n){
		if (ql<=l and r<=qr) {f(o,l,r,add,mul);return;}
		int mid=(l+r)>>1;
		push_down(o,l,r);
		if(ql<=mid) update(ql,qr,add,mul,lc(o),l,mid);
		if(qr>=1+mid)update(ql,qr,add,mul,rc(o),mid+1,r);
		push_up(o);
	}
	int query(int ql,int qr,int o=1,int l=1,int r=n){
		if (ql<=l and r<=qr) return seg[o].sum;
		int mid=(l+r)>>1,ans=0;
		push_down(o,l,r);
		if (ql<=mid) ans=(ans+query(ql,qr,lc(o),l,mid))%mod;
		if (mid+1<=qr) ans=(ans+query(ql,qr,rc(o),mid+1,r))%mod;
		push_up(o);
		return ans;
	}
}T1;
void solve(){
	int opt,x,y,k;
	cin>>opt>>x>>y;
	switch(opt){
		case 1:{
			cin>>k;
			T1.update(x,y,0,k);
			break;
		}
		case 2:{
			cin>>k;
			T1.update(x,y,k,1);
			break;
		}
		default:cout<<T1.query(x,y)<<endl;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	read();
	T1.build(1,1,n);
	while(m--) solve();
	return 0;
}

与线段树1大致相同,不再过多赘述

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

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

相关文章

ChatGLM实战:基于LangChain构建自己的私有知识库

作者简介&#xff1a;赵辉&#xff0c;区块链技术专家&#xff0c;精通各种联盟链、公链的底层原理&#xff0c;拥有丰富的区块链应用开发经验。 在之前的 ChatGLM 微调训练的实验中&#xff0c;由于数据量较小&#xff0c;调试效果并不理想。同时&#xff0c;数据需要符合 Pro…

项目管理和产品管理之间的区别

产品管理和项目管理是两个在企业中至关重要的职能部门&#xff0c;它们各自承担着不同的职责和任务。虽然两者在某些方面存在重叠&#xff0c;但它们的核心目标和方法有很大的不同。本文将对产品管理和项目管理进行详细的比较和分析。 “项目管理和产品管理有什么区别&#xff…

一周 AIGC 丨苹果下架多款 AIGC 应用,阿里云开源通义千问 70 亿参数模型

多个 AIGC 应用在苹果应用商店下架&#xff0c;包含数据采集和使用不够规范等问题。阿里云开源通义千问 70 亿参数模型&#xff0c;包括通用模型 Qwen-7 B 和对话模型 Qwen-7 B-Chat。腾讯混元大模型开始应用内测&#xff0c;内部多个业务线接入测试。百度智能云“千帆大模型平…

textarea 标签如何创建多行文本输入框?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ textarea 的写法⭐ 代码含义⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、…

看门狗文章

1. iwdg.c #include "stm32f4xx.h" #include "iwdg.h"//prer&#xff1a;预分频值 //rlr&#xff1a;自动重装载值 void IWDG_Init(unsigned char prer,unsigned int rlr)//IWDG初始化 {IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);…

Grafana V10 告警推送 邮件

最近项目建设完成&#xff0c;一个城域网项目&#xff0c;相关zabbix和grafana展示已经完&#xff0c;想了想&#xff0c;不想天天看平台去盯网络监控平台&#xff0c;索性对告警进行分类调整&#xff0c;增加告警的推送&#xff0c;和相关部门的提醒&#xff0c;其他部门看不懂…

linux网络编程--线程池UDP

目录 学习目标 1线程池 2.UDP通信 3本地socket通信 学习目标 了解线程池模型的设计思想能看懂线程池实现源码掌握tcp和udp的优缺点和使用场景说出udp服务器通信流程说出udp客户端通信流程独立实现udp服务器代码独立实现udp客户端代码熟练掌握本地套接字进行本地进程通信 1…

【网络】自定义协议 | 序列化和反序列化 | Jsoncpp

本文首发于 慕雪的寒舍 以tcpServer的计算器服务为例&#xff0c;实现用jsoncpp来进行序列化和反序列化 阅读本文之前&#xff0c;请先阅读 自定义协议 | 序列化和反序列化 | 以tcpServer为例 1.安装jsoncpp 我所用的系统是centos7.6&#xff0c;先用下面的命令查找相关的包 …

AST还原实战| 实战还原一个22M的混淆js大文件

关注它&#xff0c;不迷路。 本文章中所有内容仅供学习交流&#xff0c;不可用于任何商业用途和非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请联系作者立即删除&#xff01; 1. 需求分析 之前有星友发了这个大文件在群里: https://t.zsxq.com/10…

CountDownLatch 使用详情

CountDownLatch 是 Java.util.concurrent 包下的一个类&#xff0c;它可以用来实现一个或多个线程等待其他线程完成后再继续执行的场景。 CountDownLatch 类中有一个计数器&#xff0c;每次调用 countDown() 方法计数器的值减1&#xff0c;当计数器的值变为0时&#xff0c;调用…

面向大模型的存储加速方案设计和实践

这是 AI 大底座系列云智公开课的第三期内容。前两期我的两位同事已经向大家介绍了高性能网络和 GPU 容器虚拟化的相关内容。今天我们把目光聚焦在存储方向&#xff0c;一起来看看面向大模型的存储加速方案的设计和实践。 今天将从以下三个方面来展开这次分享&#xff1a; 介绍…

APP外包开发的Flutter框架

Flutter 是一种流行的开源UI框架&#xff0c;由谷歌开发&#xff0c;用于构建跨平台的移动应用程序。它使用一套统一的代码库&#xff0c;可以在多个平台上&#xff08;如Android、iOS、Web、桌面等&#xff09;保持一致的外观和行为。今天和大家分享一些基于 Flutter 开发的常…

CI/CD持续集成持续发布(jenkins)

1.背景 在实际开发中&#xff0c;我们经常要一边开发一边测试&#xff0c;当然这里说的测试并不是程序员对自己代码的单元测试&#xff0c;而是同组程序员将代码提交后&#xff0c;由测试人员测试&#xff1b; 或者前后端分离后&#xff0c;经常会修改接口&#xff0c;然后重新…

【CSS】说说响应式布局

目录 一、是什么 二、怎么实现 1、媒体查询 2、百分比 3、vw/vh 4、小结 三、总结 一、是什么 响应式设计简而言之&#xff0c;就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。 响应式网站常见特点&#xff1a; 同时适配PC 平板 手机等…

代码随想录 - 数组

数组是存放在连续内存空间上的相同类型数据的集合。 注意&#xff1a; 数组下标都是从0开始的。数组内存空间的地址是连续的 数组的在内存空间的地址是连续的&#xff0c;所以在删除或者增添元素的时候&#xff0c;就难免要移动其他元素的地址。 例如删除下标为3的元素&#x…

SpringMVC的注解

文章目录 前言前期准备ResponseBody 返回JSONRequestMapping 映射控制器GetMapping、PostMapping 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; SpringMVC框架只需要少量的配置即可快速实现Web应用程序开发&#xff0c;不需要大量的XML配置文件。 不…

【ES】笔记-简化对象写法箭头函数及声明特点

简化对象写法&箭头函数及声明特点 简化对象写法箭头函数及声明特点 简化对象写法 ES6 允许在大括号里面&#xff0c;直接写入变量和函数&#xff0c;作为对象的属性和方法.这样的书写更加简介 声明变量和和函数 let name南昌大学;let changefunction(){console.log(我可以改…

如何使用Kafka构建事件驱动的架构

事件驱动的架构(EDA)是一种软件设计模式&#xff0c;它关注事件的生成、检测和使用&#xff0c;以支持高效和可扩展的系统。在EDA中&#xff0c;事件是组件之间通信的主要手段&#xff0c;允许它们实时交互和响应更改。这种架构促进了松散耦合、可扩展性和响应性&#xff0c;使…

Semantic Kernel 入门系列:突破提示词的限制

无尽的上下文 LLM对自然语言的理解和掌握在知识内容的解读和总结方面提供了强大的能力。 但是由于训练数据本身来自于公共领域&#xff0c;也就注定了无法在一些小众或者私有的领域能够足够的好的应答。 因此如何给LLM 提供足够多的信息上下文&#xff0c;就是如今的LLM AI应…

el-table合并表头、动态合并列、合并尾部合计

在有些情况下&#xff0c;我们会有合并表头、合并列、合并尾部合计的需求&#xff0c;这篇文章是为了记录&#xff0c;如何进行合并&#xff0c;方便日后翻阅。 效果图 el-table合并表头 el-table合并列&#xff08;动态合并&#xff09; el-table合并尾部合计 el-table合并表…