【算法学习】线段树基础版

ops/2024/10/19 0:54:46/

一 线段树

1.概念

线段树可以理解为一个二叉树,如果是利用线段树求区间的和,那么每个结点的权值维护的是结点所维护区间的和,再将该区间一分为二,分别交由左右儿子维护。

拿区间1 - 4的和来举例子,

根结点维护的是区间1 到 4,结点权值是该区间的和,再将区间一份为二,其左儿子维护的是 1 - 2,右儿子维护的是 3 - 4 ,以此类推,直到结点维护的区间长度为1。

不难看出,每个节点的权值等于左右儿子的权值之和。

2.基本步骤

1.构造线段树

清楚了线段树的概念后,很容易构造线段树,一般算法题都是通过数组模拟二叉树,本文也采用这种方式。

struct Tree {int l, r, weight;
} tree[N];
using namespace std;void build_tree(int i, int l, int r) {if (l == r) {tree[i] = {l, r, w[l]};return;}int mid = l + r >> 1;//构建左子树build_tree(i<<1, l, mid);//构建右子树build_tree(i<<1|1, mid + 1, r);//节点权值int sum = tree[i * 2].weight + tree[i * 2 + 1].weight;//更新区间tree[i] = {l,r,sum};}
2.区间查询

从根节点开始找目标区间

1.结点维护的区间是目标区间的子集,直接返回结点权值

2.左子树与目标区间有交集,递归左子树

2.右子树与目标区间有交集,递归右子树

4.返回sum

拿查找2-3区间和举例

从根节点开始,目标区间和左子树有交集,递归左子树,目标区间和右子树有交集 ,递归右子树;

接着看根节点左儿子,目标区间只和右子树有交集,递归右子树,

看根节点右儿子,目标区间只和左子树有交集,递归左子树, 

再向下递归发现,节点维护区间刚好是目标区间的子集,直接返回权值,结束向下递归。

代码

int query(int i,int l,int r){//结点维护区间是目标区间的子集,直接返回权值if(tree[i].l>=l&&tree[i].r<=r)return tree[i].weight;int mid = tree[i].l + tree[i].r >>1;int sum = 0;if(l<=mid) sum += query(i*2,l,r);if(r>mid)sum+= query(i*2+1,l,r);return sum;
}

3.区间修改 

从根节点开始找目标区间

1.结点维护的区间是目标区间的子集,修改权值,返回

2.左子树与目标区间有交集,递归左子树

2.右子树与目标区间有交集,递归右子树

4.更新结点权值(pushup)

拿给2-3区间加1和举例

从根节点开始,目标区间和左子树有交集,递归左子树,目标区间和右子树有交集 ,递归右子树;

接着看根节点左儿子,目标区间只和右子树有交集,递归右子树,

看根节点右儿子,目标区间只和左子树有交集,递归左子树, 

再向下递归发现,节点维护区间刚好是目标区间的子集,直接修改权值,结束向下递归。

代码

void pushup(int i){tree[i].weight = tree[i<<1].weight + tree[i<<1|1].weight;
}
void modify(int i, int l, int r,int v)
{if(tree[i].l>=l&&tree[i].r<=r) tree[i].weight+=v*(tree[i].r - tree[i].l+1);else{int mid = tree[i].l + tree[i].r >>1;if(l<=mid) modify(i<<1,l,r,v);if(r>mid) modify(i<<1|1,l,r,v);pushup(i);}}

例题

1.动态求连续区间和

给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列[a,b]的连续和。

输入格式

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

第二行包含n个整数,表示完整数列。

接下来 m 行,每行包含三个整数k, a, b(k = 0,表示求子数列[a, b]的和;k = 1,表示第 a 个数加b)。

数列从1开始计数。

输出格式

输出若干行数字,表示k=0 时,对应的子数列[a, b]的连续和。

数据范围

1≤n≤100000,

1≤m≤100000,

1≤a≤b≤n,

数据保证在任何时候,数列中所有元素之和均在 int 范围内。

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
1
2
3
4
5
6
7
输出样例:

11
30
35
1
2
3

ans

#include <cstdio>
#include <iostream>
#include <algorithm>const int N = 1e5 + 10;
struct segment_tree {int l, r, weight;
} tree[N*4];
int n,m;
int w[N];
using namespace std;void build_tree(int i, int l, int r) {if (l == r) {tree[i] = {l, r, w[l]};return;}int mid = l + r >> 1;//构建左子树build_tree(i<<1, l, mid);//构建右子树build_tree(i<<1|1, mid + 1, r);//节点权值int sum = tree[i * 2].weight + tree[i * 2 + 1].weight;//更新区间tree[i] = {l,r,sum};}
void pushup(int i){tree[i].weight = tree[i<<1].weight + tree[i<<1|1].weight;
}
int query(int i,int l,int r){//结点维护区间是目标区间的子集,直接返回权值if(tree[i].l>=l&&tree[i].r<=r)return tree[i].weight;int mid = tree[i].l + tree[i].r >>1;int sum = 0;if(l<=mid) sum += query(i<<1,l,r);if(r>mid)sum+= query(i<<1|1,l,r);return sum;
}void modify(int i, int l, int r,int v)
{if(tree[i].l>=l&&tree[i].r<=r) tree[i].weight+=v*(tree[i].r - tree[i].l+1);else{int mid = tree[i].l + tree[i].r >>1;if(l<=mid) modify(i<<1,l,r,v);if(r>mid) modify(i<<1|1,l,r,v);pushup(i);}}
int main()
{scanf("%d%d", &n, &m);for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);build_tree(1, 1, n);int k, a, b;while (m -- ){scanf("%d%d%d", &k, &a, &b);if (k == 0) printf("%d\n", query(1, a, b));else modify(1, a,a, b);}return 0;
}

感谢你的阅读,希望本文对你有所帮助。 


http://www.ppmy.cn/ops/19899.html

相关文章

剑指offer--和为s的数字

题目描述&#x1f357; 输入一个递增排序的数组和一个数字s&#xff0c;在数组中查找两个数&#xff0c;使得它们的和正好是s。如果有多对数字的和等于s&#xff0c;则输出任意一对即可。 算法分析&#x1f357; 算法1&#xff1a;遍历所有的数字&#xff0c;查看其它(后面所…

数据结构 - 队列 [动画+代码注释超详解],萌新轻松上手!!!

一. 队列的概念 队列是一种特殊的线性表&#xff0c;用于存储元素&#xff0c;并且按照先进先出(First In First Out)的顺序进行管理&#xff0c;这意味着最先加入队列的元素将会是最先从队列中被移除的元素 队列的原型&#xff1a;只允许在一端进行插入数据的操作&#xff0c…

2024蓝桥杯CTF--逆向

蓝桥杯付费CT--逆向 题目&#xff1a;RC4题目&#xff1a;happytime总结&#xff1a; 题目&#xff1a;RC4 先查壳&#xff0c;无壳&#xff0c;并且是32位&#xff1a; 用32位的ida打开&#xff0c;直接定位到main函数&#xff1a; 重点关注sub_401005函数&#xff0c;这个应…

sheng的学习笔记-AI-支持向量机(SVM)

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 什么是向量机 SVM算法原理 SVM基本模型 SVM对偶问题 什么是对偶问题&#xff1a; 为什么使用对偶问题 拉格朗日定理 拉格朗日乘子法 对偶问题算法 非线性SVM算法原理 核函数 常用核函数 软间隔与正则化 软…

【国产虚拟仪器】 NI-9205模块国产替代,±10 V,250 kS/s,16位,32通道C系列电压输入模块

10 V&#xff0c;250 kS/s&#xff0c;16位&#xff0c;32通道C系列电压输入模块 ​NI‑9205​可​执行​单​端​或​差分​模拟​输入&#xff0c;​每​个​具有​四​个​可​编​程​的​输入​范围。 该​模​块​以​较​低​的​价格​提供​了​高​通道​数​和​高…

OneFlow新概念清单,AI深度学习的革命性突破(AI写作)

首先&#xff0c;这篇文章是基于笔尖AI写作进行文章创作的&#xff0c;喜欢的宝子&#xff0c;也可以去体验下&#xff0c;解放双手&#xff0c;上班直接摸鱼~ 按照惯例&#xff0c;先介绍下这款笔尖AI写作&#xff0c;宝子也可以直接下滑跳过看正文~ 笔尖Ai写作&#xff1a;…

python三维交互可视化工具plotly使用

三维数据可视化工具使用 import plotly.graph_objects as go import numpy as np# 生成随机点 data np.random.uniform(-3,3,(100000, 2)) Z np.exp(-((data[:, 0] - 0)**2 / (2*1**2) (data[:, 1] - 0)**2 / (2*1**2)))scatter1 go.Scatter3d(xdata[:, 0], ydata[:, 1], …

网络安全之SQL注入及防御(下篇)

目录 什么是SQL注入&#xff1f; 一&#xff0c;SQL注入会导致什么呢&#xff1f; 二&#xff0c;SQL注入思想与步骤 三&#xff0c;SQL注入的绕过 四&#xff0c;sqlmap工具的使用 五&#xff0c;sql注入的防御方法 总结 什么是SQL注入&#xff1f; SQL注入&#xff08;…