线段树复习笔记
\(\quad\)线段树是 \(\rm OI\) 中最重要的数据结构,有很多分支,非常重要。
\(\large\rm 普通线段树\)
\(\quad\)建立一些结点,每个节点保存序列中一段的信息,形成满二叉树的结构。
\(\large\rm 动态开点\)
\(\quad\)我们发现如果对所有位置都建立一个节点太浪费空间,于是不让子节点为父节点编号的两倍,而是在需要的时候给子节点分配编号,对每个节点记 \(\rm lc\) 和 \(\rm rc\),然后记 \(\rm cnt\) 表示当前使用到哪一个结点,这样就可以实现了。
\(\large\rm 权值线段树\)
\(\quad\)将权值作为下标,节点内存储的是这个权值有多少个。大多数时候值域很大,所以要使用动态开点或离散化。
\(\large\rm 线段树合并\)
\(\quad\)一般是将一颗线段树合并到另一颗上。实现时同时枚举两颗线段树的结点,如果当前有一个节点不存在,就令被合并线段树当前结点为存在的那个结点,否则递归。
\(\large\rm 扫描线\)
\(\quad\)由于扫描线一般都是用线段树维护,所以放在这里面讲。
\(\quad\)一般用于维护偏序关系,枚举其中一维有序状态,用线段树维护另一维。
\(\quad\)三维偏序关系还可以套一个 cdq 分治
\(\large\rm [P5490]扫描线\)
\(\quad\)扫描线模板题,从小到大枚举横线的纵坐标,枚举到矩形下界就给这条边的左右端点构成的区间 \(+1\),否则 \(-1\),用线段树维护,当前线段树有值的结点个数就是这个纵坐标的贡献。
\(\quad\)怎么维护线段树中不为 \(0\) 的位置数量呢?我们发现在做扫描线的时候权值不会小于 \(0\),于是我们考虑维护区间最小值和最小值的数量,这个在合并的时候,如果左右儿子的最小值相同,则将其最小值数量加起来,否则继承最小值更小的那个的最小值数量。
\(\quad\)当然,本题还需要动态开点,所以需要维护区间内有效点的数量。
\(\quad\)时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [SPOJ]GSS-I\)
\(\quad\)先求出前缀和,发现答案为区间最大值减最小值,但需要满足最小值在最大值右边。
\(\quad\)对线段树的每个节点维护最小值,最大值和答案,发现答案可以从两棵子树的答案更新,也可以更新为右子树中的最大值减去左子树中的最小值。
\(\quad\)于是直接用线段树即可维护,时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [SPOJ]GSS-III\)
\(\quad\)发现这题是带修版的 \(\rm GSS1\),但是由于前缀和无法动态修改,于是考虑直接维护区间和。
\(\quad\)显然要维护区间和以及最大子段和,发现最大子段和需要通过左儿子的最大后缀和和右儿子的最大前缀和更新,于是再记最大前后缀和,而最大前后缀和是可以通过区间和和子节点的最大前后缀和计算的,于是这题就可以用线段树做了。
\(\quad\)时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [SPOJ]GSS-IV\)
\(\quad\)我们发现,一个区间内如果全都是 \(1\) 或 \(0\),那么对它开方是没有意义的,所以记录最大值,如果最大值为 \(0\) 或 \(1\),那么直接跳过这个区间。
\(\quad\)我们发现这样做每个点最多被修改 \(7\) 次,故时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [SPOJ]GSS-V\)
\(\quad\)我们发现这个问题和 \(\rm GSS-I\) 很像,唯一的区别是左右两边限定了区间。我们发现如果两个区间不重合,那就是右区间最大值减左区间最小值。
\(\quad\)若两区间重合,令从左到右三个区域分别为 \(\rm I,II,III\),首先用 \(\rm \max\{II\cup III\}-\min\{I\}\) 更新答案,然后用 \(\rm \max\{III\}-\min\{I\cup II\}\) 更新答案,最后只剩下两个端点都在 \(\rm II\) 内的答案,这个就是 \(\rm GSS-I.\)
\(\quad\)时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [P4198]楼房重建\)
\(\quad\)神题。
\(\quad\)首先肯定将每个楼房的信息转化为斜率。显然要维护区间最大值和从这个区间左端点开始能看到多少栋楼。
\(\quad\)现在考虑将左右区间合并,显然要先将左区间的答案加上,然后考虑右区间能看到多少个点。
- 如果左区间的最大值小于右区间的左区间的最大值,那么肯定对右区间的右区间没有影响,将右区间的右区间的答案加上,左区间递归。
- 如果左区间的最大值大于右区间的左区间的最大值,那么右区间的左区间肯定一个都看不到,直接将右区间的右区间递归下去。
\(\quad\)单次合并时间复杂度 \(\Theta(n\log n)\),总时间复杂度 \(\Theta(n\log ^2n).\)
\(\quad\rm PS:\) 这题有个坑点,计算右区间的右儿子的答案的时候不能写成 ans[RS]
,而是要写成 ans[p] - ans[LS]
,因为右儿子的答案可能会被左儿子挡掉一些,这些还是不能算进去。
\(\large\rm [P1502]窗口的星星\)
\(\quad\)考虑将每颗行星变成一个带权矩形,我们发现最大亮度和就是最大矩形交的权值之和。
\(\quad\)考虑用扫描线,维护区间最大值即可。
\(\quad\)坑点 \(1:\) 由于边界上的点不用算,所以矩形右上角为 \((x+w-1,y+h-1).\)
\(\quad\)坑点 \(2:\) 我们在每次修改过后都统计了一遍答案,所以当线段纵坐标相同的时候,删除要排在前面,防止统计了一个不合法的大的答案。
\(\large\rm [CF240F]TorCoder\)
\(\quad\)考虑一次操作干了什么事情,若存在一个以上字母出现了奇数次,则无法重排,否则将出现奇数次的字母放在最中间,然后将所有字母按照从小到大再从大到小的顺序放在这个字母的两边。
\(\quad\)于是可以对每个字母开一颗线段树,维护区间赋值操作和区间求数量操作即可。
\(\quad\)时间复杂度 \(\Theta(n\log n|\Sigma|).\)
\(\large\rm [CF242E]XOR~on~Segment\)
\(\quad\)考虑对每一位开一颗线段树,问题就变成了维护一个 \(0/1\) 序列,支持区间取反和区间求和。
\(\quad\)时间复杂度 \(\Theta(n\log^2n).\)
\(\large\rm [CF414C]Mashmokh~and~Reverse~Operation\)
\(\quad\)我们发现这个题目中区间的结构很像一颗线段树,于是考虑它的性质。
\(\quad\)发现交换一个翻转一个区间等价于将它的两个子区间交换,然后递归操作。而一个区间的逆序对数量为两个子区间之间的逆序对数量加上两个子区间内部的逆序对数量。
\(\quad\)于是考虑维护每一层每一个结点的两个子区间之间的逆序对数量之和。这样,修改操作就变成了将某一层下面的所有区间翻转,由于层数只有 \(20\),所以直接维护每一层是否被翻转即可。求当前的答案只需要预处理每一层的所有节点的两个子区间之间的答案之和即可,发现一层区间一定是一起翻转,于是每一层只有两种状态,分别预处理即可。
\(\quad\)时间复杂度 \(\Theta[(2^n+m)n].\)
\(\large\rm [CF446C]DZY~Loves~Fibonacci~Numbers\)
\(\quad\)截取 \(\rm CF\) 官方题解 :
\(\quad\)As we know, \(F_{n}=\frac{\sqrt{5}}{5}\left[\left(\frac{1+\sqrt{5}}{2}\right)^{n}-\left(\frac{1-\sqrt{5}}{2}\right)^{n}\right]\)
\(\quad\)Fortunately, we find that \(383008016^{2} \equiv 5\left(\bmod 10^{9}+9\right)\)
\(\quad\)So, \(383008016 \equiv \sqrt{5}\left(\bmod 10^{9}+9\right)\)
\(\quad\)With multiplicative inverse, we find,
\(\quad\frac{\sqrt{5}}{5} \equiv 276601605\left(\bmod 10^{9}+9\right)\)
\(\quad\frac{1+\sqrt{5}}{2} \equiv 691504013\left(\bmod 10^{9}+9\right)\)
\(\quad\frac{1-\sqrt{5}}{2} \equiv 308495997\left(\bmod 10^{9}+9\right)\)
\(\quad\)Now, \(F_{n} \equiv 276601605\left(691504013^{n}-308495997^{n}\right)\left(\bmod 10^{9}+9\right)\)
\(\quad\)于是我们发现只需要分别维护两个序列,在一个序列中公比相同,故只需要维护首项,后面的数可以直接算出来。下传的时候给左子区间直接加上这个首项,右子区间加首项乘上 \(q^{\frac{r-l+1}{2}}\) 即可。
\(\quad\)时间复杂度 \(\Theta(n\log n).\)
\(\large\rm [CF522D]Closest~Equals\)
\(\quad\)我们发现,如果记 \(p_i\) 表示距离 \(i\) 最近的在 \(i\) 之前的与 \(i\) 颜色相同的点与 \(i\) 的距离,那么对于一个询问 \([l,r]\),其答案为所有满足 \(i-p_i\geqslant l\) 和 \(i\leqslant r\) 的点中,\(p_i\) 最小的那个。
\(\quad\)于是考虑将坐标离散化,然后将询问离线下来,按右端点排序,一个个枚举 \(i\),若 \(p_i\not =0\),则将 \(C_{i-p_i}\) 的值与 \(p_i\) 取 \(\min\),可以发现,\(C\) 中区间 \([l,r]\) 内的最小值就是这个区间的答案。
\(\quad\)单点修改,区间查询,可以使用线段树维护。可以发现 \([l,r]\) 的答案和 \([l,n]\) 的答案是相同的,于是也可以使用树状数组维护后缀和。
\(\quad\)时间复杂度 \(\Theta(n\log n).\)
原文地址:https://www.cnblogs.com/C-C-A/p/15102202.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 数组属性和方法
- laravel框架实现敏感词汇过滤功能示例
- python 接收处理外带的参数方法
- Django框架实现的简单分页功能示例
- php测试kafka项目示例
- Python读取YUV文件,并显示的方法
- php-7.3.6 编译安装过程
- PHP查找一列有序数组是否包含某值的方法
- python 从文件夹抽取图片另存的方法
- laravel框架使用极光推送消息操作示例
- 对pycharm 修改程序运行所需内存详解
- Python小工具之消耗系统指定大小内存的方法
- Thinkphp框架+Layui实现图片/文件上传功能分析
- PHP实现单例模式建立数据库连接的方法分析
- 解决Python2.7中IDLE启动没有反应的问题
- PHP中mysqli_get_server_version()的实例用法