P3372 【模板】线段树 1(内附封面)

【模板】线段树 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 n≤8, m ≤ 10 m \le 10 m≤10。

对于 70 % 70\% 70% 的数据: n ≤ 10 3 n \le {10}^3 n≤103, m ≤ 10 4 m \le {10}^4 m≤104。

对于 100 % 100\% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1≤n,m≤105。

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

【样例解释】

模板题,思路不过多解释

线段树由很多部分共同实现,比如单点查询,单点修改,区间修改,区间查询等

建树

首先,线段树是二叉树,因此具有二叉树的性质,其左儿子节点与右儿子节点是固定的,具体实现如下,其中, l c ( x ) lc(x) lc(x)为左儿子, r c ( x ) rc(x) rc(x)为右儿子

cpp 复制代码
#define lc(x) (x<<1)
#define rc(x) ((x<<1)|1)

其次,线段树的建立为递归建立,最底层的节点对应的就是 a [ 1... n ] a[1...n] a[1...n]

cpp 复制代码
void build(int x,int l,int r){
	tag[x]=0;
	if(l==r){
		sm[x]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lc(x),l,mid);
	build(rc(x),mid+1,r);
	sm[x]=sm[lc(x)]+sm[rc(x)];
}

s m [ x ] = s m [ l c ( x ) ] + s m [ r c ( x ) ] sm[x]=sm[lc(x)]+sm[rc(x)] sm[x]=sm[lc(x)]+sm[rc(x)]通常会被单独写做一个函数pushup

区间修改与查询

单点修改与查询只需如同建树一样查找到节点修改并pushup或return即可,不过多赘述。

对于区间修改,我们需要用到 lazy_tag 对于一次修改操作我们先不全部进行修改,当火烧眉毛不得不用到这个值时再进行修改,使用一个tag[]数组实现

cpp 复制代码
void cover(int x,int l,int r,int d){//打上tag
	sm[x]+=(r-l+1)*d;
	tag[x]+=d;
}
void pushdown(int x,int l,int r){//tag下传
	if(tag[x]){
		int mid=(l+r)>>1;
		cover(lc(x),l,mid,tag[x]);
		cover(rc(x),mid+1,r,tag[x]);
		tag[x]=0;
	}
}
void modify(int x,int l,int r,int L,int R,int d){//区间修改
	if(l>=L&&r<=R) {
		cover(x,l,r,d);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if(L<=mid) modify(lc(x),l,mid,L,R,d);
	if(R>mid) modify(rc(x),mid+1,r,L,R,d);
	sm[x]=sm[lc(x)]+sm[rc(x)];
}
int query(int x,int l,int r,int L,int R){//区间查询
	int res=0;
	if(l>=L&&r<=R)return sm[x];
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if(L<=mid)res+=query(lc(x),l,mid,L,R);
	if(R>mid) res+=query(rc(x),mid+1,r,L,R);
	return res;
}

AC CODE

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long int
#define lc(x) (x<<1)
#define rc(x) ((x<<1)|1)
const int N=1e6;
int sm[N<<2],tag[N<<2],a[N<<2];
void build(int x,int l,int r){
	tag[x]=0;
	if(l==r){
		sm[x]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lc(x),l,mid);
	build(rc(x),mid+1,r);
	sm[x]=sm[lc(x)]+sm[rc(x)];
}
void cover(int x,int l,int r,int d){
	sm[x]+=(r-l+1)*d;
	tag[x]+=d;
}
void pushdown(int x,int l,int r){
	if(tag[x]){
		int mid=(l+r)>>1;
		cover(lc(x),l,mid,tag[x]);
		cover(rc(x),mid+1,r,tag[x]);
		tag[x]=0;
	}
}
void modify(int x,int l,int r,int L,int R,int d){
	if(l>=L&&r<=R) {
		cover(x,l,r,d);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if(L<=mid) modify(lc(x),l,mid,L,R,d);
	if(R>mid) modify(rc(x),mid+1,r,L,R,d);
	sm[x]=sm[lc(x)]+sm[rc(x)];
}
int query(int x,int l,int r,int L,int R){
	int res=0;
	if(l>=L&&r<=R)return sm[x];
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if(L<=mid)res+=query(lc(x),l,mid,L,R);
	if(R>mid) res+=query(rc(x),mid+1,r,L,R);
	return res;
}
int n,m;
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int o;
		cin>>o;
		if(o == 1){
			int l,r,k;
			cin>>l>>r>>k;
			modify(1,1,n,l,r,k);
		}
		if(o == 2){
			int l,r;
			cin>>l>>r;
			cout<<query(1,1,n,l,r)<<endl;
		}
	}
	return 0;
}

附封面(虽然我不是三玖党,但是三玖天下第一,三玖不赢还能谁赢啊喂!)

四叶啊,那没事了

找封面的时候找到两张好图,就一起放力www

相关推荐
共享家95277 分钟前
LRU 缓存的设计与实现
开发语言·c++
夏鹏今天学习了吗11 分钟前
【LeetCode热题100(64/100)】搜索旋转排序数组
算法·leetcode·职场和发展
2301_7965125232 分钟前
Rust编程学习 - 问号运算符会return一个Result 类型,但是如何使用main函数中使用问号运算符
开发语言·学习·算法·rust
草莓熊Lotso37 分钟前
Linux 基础开发工具入门:软件包管理器的全方位实操指南
linux·运维·服务器·c++·人工智能·网络协议·rpc
小龙报1 小时前
算法通关指南:数据结构和算法篇 --- 队列相关算法题》--- 1. 【模板】队列,2. 机器翻译
c语言·开发语言·数据结构·c++·算法·学习方法·visual studio
晨非辰1 小时前
【数据结构初阶】--从排序算法原理分析到代码实现操作,参透插入排序的奥秘!
c语言·开发语言·数据结构·c++·算法·面试·排序算法
三川6982 小时前
排序算法介绍
数据结构·算法·排序算法
2301_795167205 小时前
玩转Rust高级应用 如何避免对空指针做“解引用”操作,在C/C++ 里面就是未定义行为
c语言·c++·rust
智驱力人工智能7 小时前
基于视觉分析的人脸联动使用手机检测系统 智能安全管理新突破 人脸与手机行为联动检测 多模态融合人脸与手机行为分析模型
算法·安全·目标检测·计算机视觉·智能手机·视觉检测·边缘计算
2301_764441337 小时前
水星热演化核幔耦合数值模拟
python·算法·数学建模