线段树
时间:2020-05-18
本文章向大家介绍线段树,主要包括线段树使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1、什么是线段树?
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
2、构建线段树
通过像二叉树一样递归建树,但需要记录做边界和右边界。
3、lazy
即懒标记,我们的加减、乘除都是区间修改操作,如果将线段树拆开在每个叶子结点上面进行修改再维护,最坏情况下我们修改的复杂度就变成了O(mnlogn),十分慢。于是我们引入懒标记。每一个线段树的结点都会有一个加法的标记和乘法的标记,如果我们想要修改的区间覆盖了结点的对应区间,我们不对其中的每一个元素进行修改,而是只更新两个标记和总值,这样就巧妙地优化了程序的复杂度。
4、区间修改
判断要进行修改的区间是不是包括了这个结点的区间,如果包括了,就直接修改这个点的两个标记和总值。如果没有包括,就往这个节点的孩子递归,直到找到被包括的区间进行修改。进行修改时,为了方便,可以先把加法标记进行相乘,然后使用乘法标记,简单来说,就是(sum+lazy_+)*lazy_x=sum*lazy_x+lazy_+*lazy_x。
5、区间查询
思路和区间修改相类似,从第一个结点开始 ,如果结点被覆盖,就返回它维护的区间和sum。
代码
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; long long n,m,k,x,y; long long a[100000]; struct tree{ long long l,r,sum,tag; }t[400000]; long long ls(long long rt){return rt << 1;} long long rs(long long rt){return rt << 1 | 1;} void build(long long rt,long long l,long long r){ t[rt].l = l,t[rt].r = r; if(l == r){ t[rt].sum = a[l]; return; } long long mid = (l + r) >> 1; build(ls(rt),l,mid); build(rs(rt),mid+1,r); t[rt].sum = t[ls(rt)].sum + t[rs(rt)].sum; } //root void push_down(long long rt){ t[ls(rt)].sum += t[rt].tag * (t[ls(rt)].r - t[ls(rt)].l + 1); t[rs(rt)].sum += t[rt].tag * (t[rs(rt)].r - t[rs(rt)].l + 1); t[ls(rt)].tag += t[rt].tag; t[rs(rt)].tag += t[rt].tag; t[rt].tag = 0; } void change(long long rt,long long l,long long r,long long x){ if(l <= t[rt].l && r >= t[rt].r){ t[rt].sum += (t[rt].r - t[rt].l + 1) * x; t[rt].tag += x; return; } if(t[rt].tag) push_down(rt); if(l <= t[ls(rt)].r){ change(ls(rt),l,r,x); } if(r >= t[rs(rt)].l){ change(rs(rt),l,r,x); } t[rt].sum = t[ls(rt)].sum + t[rs(rt)].sum; } long long check(long long rt,long long l,long long r){ if(l <= t[rt].l && r >= t[rt].r){ return t[rt].sum; } if(t[rt].tag) push_down(rt); long long res = 0; if(l <= t[ls(rt)].r){ res += check(ls(rt),l,r); } if(r >= t[rs(rt)].l){ res += check(rs(rt),l,r); } return res; } int main(){ cin >> n >> m; for(long long i = 1;i <= n; i++) cin >> a[i]; build(1,1,n); for(long long i = 1;i <= m; i++){ cin >> k; if(k == 1){ cin >> x >> y >> k; change(1,x,y,k); }else{ cin >> x >> y; cout << check(1,x,y) << endl; } } return 0; }
借鉴大佬:https://www.cnblogs.com/Cao-Yucong/p/12164576.html
原文地址:https://www.cnblogs.com/zhaoxuelin/p/12912696.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Hacking with iOS: SwiftUI Edition - Hot Prospects项目(二)
- HarmonyOS-对Android开发者也太友好了吧
- 你还在使用复杂的 zkclient 开发 zookeeper 么?是时候用 Curator 了 !
- 如何通俗理解类和类型的差别?
- 如何理解变量?
- 装逼篇 | 抖音超火的九宫格视频是如何生成的,Python 告诉你答案
- 能否详细讲讲字符串呢?
- 能否一次性帮我把数组讲明白?
- 什么是装箱和拆箱?
- ceph常用命令
- 如何创建一个不受长度限制的数组?
- 数组、列表及字符串如何相互转换?
- 批量ping-shell
- 能否详细介绍一下判断语句?
- IF ELSE IF 到底算不算分支语句?