szu 寒训个人复习第一天 线段树入门单点修改,区间修改,以及线段树的扩展运用[线段树+dp][区间最大公约数]
寒讯内容有点过多(其实是我太菜了)水一波怕忘了(人老了)**什么是线段树**
线段树是本蒟蒻感觉用处特别大的算法
那么线段树上面的节点表示什么意思呢?
线段树,上面的节点表示一个区间,父亲节点表示的区间是左右儿子相加.
同一层的节点所代表的区间,相互不会重叠.一层节点所代表的区间,加起来是个连续的区间
下面来张图看看线段树的建树过程
该图片来自秦淮案大佬的博客
那么这么牛叉的数据结构有什么用处呢?
通过观察可以发现线段树将一个连续的区间切割成了一个个子区间那么它的作用当然是和区间的操作有过:比如说区间的四则运算,区间值数值查询…(我目前就会这么多。。)
下面来贴代码啦(。——。)
千里之行始于建树
/先来一波准备操作/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cmath>
#include <cctype>
#include <vector>
#include <map>
#include <stack>
#include <limits.h>
#include <queue>
#include <deque>
#include <list>
#define root rt;//宏定义左右儿子
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define m(a,b) memset((a),(b),sizeof(a))
using namespace std;typedef long long ll;
typedef pair<int,int> PII;
const int M = 5e4 + 10;
int temp;
int ps[M << 2];//底层子节点
ll sum[M << 2];//四倍空间
这里要提一下就是线段树的空间问题
通过观察可知加入线段树有n层
那么边数就是2^0 + 2 ^ 1 + 2 ^ 2 + …2 ^ n - 1 = 2 ^ n - 1;(等差数列求和)
那么底层的子节点有 2 ^ n - 1;
按理说 空间 = 底层节点 * 2(????)
不幸的是本蒟蒻第一次开就RE了(自闭 )
下面是某位大佬的证明
假设我们N个连续的节点需要建立线段树,如果最下层有节点,那么最左边一定不是空的。而最左边节点也就是左儿子是:[l,(l+r)>>1][l,(l+r)>>1]。因此就是(1+N)(1+N)不停/2/2。那么线段树的深度为log21+Nlog21+N
假如是满二叉树,那么节点数为
2^(log21+N)
好吧我也看不懂反正开四倍就对了(滑稽.jpg)
这是建树阶段咯
建树口诀 ->build(左右儿子) ->push_up(爸爸);
inline void Push_up(int rt)//有点像分治法
{sum[rt] = sum[rt << 1] + sum[rt << 1|1];//这里是位运算//左移相当*2 与 相当+1
} inline void build(int rt, int l, int r)
{if(l == r){sum[rt] = ps[l];return ;}build(Lson);//先更新左右儿子;build(Rson);Push_up(rt);//更新后别忘了pust_up更新
}
下面先来一波常规操作
单点修改gen
单点修改口诀 -> 等改回 -> 二分查-> 更父值//最后一步个人认为比较容易忘
inline void Change(int rt, int l, int r, int x, int v)
//就是将 x 点 的权值修改为 v;
{if(l == r)//树的层次遍历{//早到这个点就改使劲改 改完就跑sum[rt] += v;return ;} if(x <= mid) Change(Lson,x,v);//通过区间的找爸爸的儿子在哪 类比二三分理解else Change(Rson,x,v);Push_up(rt);
}
如果我们要查询呢?
因为父节点就存储了子节点的查询性质那么我们只要将查询的区间分割
再分别递归查询
inline ll Query_sum(int rt, int l, int r, int posl, int posr)
// 后面两个是当前的位置
{int ans = 0;if(posl <= l && r <= posr) //目标区间包含了当前区间return sum[rt];if(posl <= mid)// 目标区间有一部分在右边 ans += Query_sum(Lson,posl,posr);if(posr > mid)//目标区间有一部分在右边 ans += Query_sum(Rson,posl,posr);Push_up(rt);return ans;
}
(好啦今天就这样吧太晚了,明天再更区间修改,用空水一波自己没想出来的题解);
持续更新…
(吐槽一下 csdn的夜间审核功能------比较慢)
线段树的区间修改问题
首先我们引入这样一个问题:我们要对线段树的一段区间的数值进行修改(比如说 + 上delta),如果我们每一个点都去修改线段树的时间复杂度就会退化到O(nlogn)【因为递归(logn)* 修改(n】
比直接建数组修改数组时间O(n)还可怕,这是绝大多数算法题不可以接受的时间复杂度,那么线段树就有一个创新性的用法:(懒惰标记)。
下面来看一张图理解一下
假如我们想将区间[3,7]的值都加上delta,我们可以发现[3,7]可以拆分成[3,4],[5,6],[7,7]而[3,4],[5,6],[7,7]分别对应上图中的5,6,14号节点,因此我们只需要将这几个节点进行修改。懒惰标记用于存储父节点的修改信息, 但暂时不把信息传给子节点,等到需要用到子节点时再把信息传给子节点。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <vector>
#include <map>
#include <stack>
#include <limits.h>
#include <queue>
#include <deque>
#include <list>
#define root rt;
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int M = 1e5 + 10;ll sum[M << 2];
ll num[M << 2];
ll tag[M << 2];//懒惰标记
inline void push_up(int rt)
{sum[rt] = sum[rt << 1]+ sum[rt << 1 | 1];
}inline void Build(int rt,int l, int r)
{if(l == r){sum[rt] = num[l];return;}Build(Lson);Build(Rson);push_up(rt);
}
建树上和单点修改的没有什么区别定义那里多定义了一个tag数组是用来懒惰标记的,精华在下面这一坨里面;
首先看一下push_down函数
( 顾名思义(就是信息向下传输))
我们看一下操作顺序
1.首先判断一下父节点上是否又标记就是区间修改的信息
2.将父节点的tag值传给子节点~~(注意一下这里是 tag[left] += lazy, 我之前老是打成=号qwq)~~
3.更新一下左右儿子的sum = lazy * (区间长度 || 儿子的个数)
4.记得哦 因为你把父节点的数值传递下去了 所以要把父节点的tag变成0
防止重复计算
inline void push_down(int rt, int l, int r)
{//精髓在与只递归被父节点覆盖的下一层 if(tag[rt])//如果父节点被覆盖{ll lazy = tag[rt];int left = rt << 1, right = rt << 1|1;//左右孩子加上父节点的tag值 tag[left] += lazy,tag[right] += lazy;sum[left] += lazy * (mid - l + 1);sum[right] += lazy * (r - mid);tag[rt] = 0;//把父节点的值清空 防止 重复加 }
}
// 将区间[start,end]的值都加上delta
// 如果当前区间[l,r]是[start,end]的真子集,那么直接修改当前节点对应的区间不用递归向下修改了
// 记住,我们修改的值包括区间和(tree数组)和延迟标记(tag数组)
// 否则我们需要递归查找当前节点rt的左右孩子,在进行递归查找之前我们需要先将当前节点已经存在的延迟标记传下去(push_down函数)。
// 然后再根据左子树是否有目标区间的元素和右子树是否有目标区间的元素进行递归修改。
// 最后不要忘记更新当前节点的值
inline void updateRange(int rt,int l,int r, int posl,int posr,ll add)
{// posl 是你要修改的区间长度下标 if(posl <= l && posr >= r)//如果当前的位置被查询的区间赋盖{sum[rt] += add * (r - l + 1);//区间长度tag[rt] += add; //表示下面的点的都要加add//待会要查询的时候直接 把tag的值传下去 return ; } push_down(rt,l,r);if(posl <= mid) updateRange(Lson,posl,posr,add);if(posr >= mid + 1) updateRange(Rson,posl,posr,add);push_up(rt);
}
再来看undateRange函数
1.判断一下当前递归的区间是否被要求修改的区间全部包含
if包含了就直接修改该节点,改完就return
else 关键一步就是push_down 就是将父节点的tag传下去告诉儿子爸爸来了
2.再去递归修改
再来看看区间询问
inline ll rangeQuery(int rt,int l,int r, int posl,int posr)
{if(posl <= l && posr >= r)return sum[rt];push_down(rt,l,r);//如果还没有包含//就要继续向下递归但是向下递归的时候要把父节点的tag值传下去 ll res = 0;if(posl <= mid)//虽然没有覆盖到 全部但是覆盖了子树res += rangeQuery(Lson,posl,posr);if(posr >= mid + 1)res += rangeQuery(Rson,posl,posr);return res; }
1.首先依旧是判断是否包含
如果包含了就直接return sum[rt];
2.如果没有就要继续向下递归但是向下递归的时候要把父节点的tag值传下去
3.定义一个res值去接受子节点的信息 并且存储答案
这里补充一点 所有的函数都有(rt, l, r)除了push_up
szu线段树练习题解个人版
poj2528
Mayor’s Poster
题目大意是说,有一堵长为10000000,现在要在墙上贴
szu 寒训个人复习第一天 线段树入门单点修改,区间修改,以及线段树的扩展运用[线段树+dp][区间最大公约数]相关推荐
- szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】
树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...
- szu 寒训复习day #4数论入门详解[修改]用Mardown再改了一下
整数的取余运算 定义:带余除法 设a,ba,ba,b是整数,且b>0b>0b>0,则存在非负整数 q,rq,rq,r ,使得 a=bq+ra=bq+ra=bq+r 且 0≤r< ...
- szu 寒训 day#3 ST表 和 LCA问题 附例题 菜鸡解法
昨天我们讲述了树状数组今天我们来讲ST表(解决静态RMQ (Rang Minmum/Maximum Query)问题的数据结构) 假如说我们暴力去查询区间的最值得话每次操作都是O(n) 如果询问次数跟 ...
- 操作系统王道考研复习——第一章(计算机系统概述)
操作系统王道考研复习--第一章(计算机系统概述) 计算机系统概述 1.1操作系统的基本概念 1.1.1 操作系统的概念 1.1.2 操作系统的特征 1. 并发 2. 共享 3. 虚拟 4. 异步 1. ...
- 概率复习 第一章 基本概念
本文用于复习概率论的相关知识点,因为好久不接触了,忘了不少.这里捡起来,方便学习其他知识. 总目录 概率复习 第一章 基本概念 概率复习 第二章 随机变量及其分布 本章目录 事件的运算 交换律 结合律 ...
- 并行计算复习————第一篇 并行计算硬件平台:并行计算机
并行计算复习 第一篇 并行计算硬件平台:并行计算机 Ch1 并行计算与并行计算机结构模型 1.1多核处理器与线程级并行 1.何谓多核处理器? 将功能复杂的单一核处理器划分为若干个功能相对简单的多个处理 ...
- 网络经济学期末复习——第一章 导论
网络经济学期末复习 --第一章.导论 一.课程介绍 二.课本选择 三.思维导图--第一章 导论 四.温馨提示 一.课程介绍 网络经济学简介: 网络经济学是近几年的一门新兴的研究网络环境下经济活动的交叉 ...
- 初中计算机知识点结构图,初中信息技术总复习第一课 构建知识框架
(一)设计思路: 本节课的教学内容根据课标和中考的要求,在教师的帮助下,引导学生梳理教材,构建知识框架.通过分工协作.探讨交流.归纳总结,能够构建三年来学过的知识框架,以便为后期的专项复习打下基础.在 ...
- 平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小。
题目:平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小. 源码如下: 1 #include <iostream> 2 #include <string.h> 3 ...
最新文章
- 《PLACEBO》(安慰剂)米津玄師/野田洋次郎 (罗马音、歌词、汉译)
- PhpStorm 注册相关
- matlab中asix off_matlab中 hold on 与hold off的用法
- APE结合键盘控制角色运动 转
- 用计算机写试卷反思,计算机试卷
- Codeforces Round #354 (Div. 2) A. Nicholas and Permutation
- 训练 AI 创作诗歌 6 NLP 从零到英雄 Training an AI to create poetry
- sql注入开源网站包
- JZ2440开发板nand flash出现坏块的解决办法
- libxml2 安装使用
- Flutter 多渠道打包详解(埋点统计系列文章)
- springboot依赖lombok插件、lombok常用注解
- 新视野大学英语(第三版)视听说4Unit7必修答案
- 为别人着想,是最大的利己
- Oracle 批量插入(insert all into)
- python入门day06列表
- oracle ora-各种常见java.sql.SQLException归纳
- 团战可以输、提莫必须死 oj
- 欧姆龙温控器参数笔记(五)(高级功能设定菜单)
- 国家/地区语言缩写代码