首页 > 其他分享 >浅谈线段树

浅谈线段树

时间:2024-11-21 16:33:17浏览次数:1  
标签:浅谈 标记 线段 矩阵 区间 最大值 最值

1 前言

线段树一直是高频考点,可以直接出也可以作为数据结构优化其他算法。这里我只想说说线段树的基本理解以及如何构造,也就是如何写出信息和标记,信息之间的合并,标记之间的复合,信息和标记之间的复合。以及矩阵的辅助理解,区间最值、历史版本相关问题。

2 线段树

线段树运用了分治的思想,将每个大区间分成两个小的区间。每个区间都有自己的信息,为儿子两个区间信息的合并。

2.1 单点修改

如果我们有这样一个问题:

  1. 单点加
  2. 区间和

在线的情况下,如果是暴力,单点修改是 \(O(1)\),但区间和是 \(O(n)\)。而用了线段树就将复杂度平均了,均为 \(O(\log n)\)。

这是单点修改的情况。但出现区间修改,定位到每个点的话,单次复杂度就是 \(O(n\log n)\),不如暴力。

2.2 区间修改

我们先需要找到线段树上所有完全包含于目标区间的极大区间,修改这些即可。此时要是能用线段树,信息要能够直接计算出来。至于上面的节点回溯时合并信息即可。

如果所有询问都与这些极大区间以下的节点无关,那么答案还是正确的。

但是下面的节点怎么办?它们没有立马被修改操作所影响。此时可以在极大区间上打一个标记,表示该区间以下的所有节点都没有被标记所影响。

如果询问或修改下面的节点,就把标记下传,同时更新出应有的信息,此时的答案和修改就是正确的。

这样就将修改均摊给了所有询问。但此时也面临着问题。我们需要找到一种标记的描述,使得任何时刻它都等价于还未下传的修改造成的影响,下传后能够直接计算出子节点当前的信息。同时,如果下传时遇到了另一个标记(此时一定有先后顺序,由标记的下传的时刻可以得出,即下传的一定在后面),那么两个标记要能够复合成一个,这个新的标记等价于两个标记先后对区间的影响。

如果能够找到这样的标记,可以与信息复合、与标记复合,那么就可以用普通线段树实现。

2.3 标记与信息

抽象的说,信息与标记都是一个半群,设信息为 \(D\),标记为 \(M\),而我们要思考的就是 \(D+D\rightarrow D\),\(D\times M\rightarrow D\),\(M\times M\rightarrow M\)。

有些题目难在信息的合并,有些难在标记的复合。而标记的复合一般来说会更难,因为并不是简单的相加,而是将两个标记融为一体,如果你的标记内容过少,就会难以复合成新的标记。

信息一般至少要有询问的答案,而复合一般是增量。举个不算难的例子:

  1. 区间加
  2. 区间乘
  3. 区间覆盖
  4. 区间询问和

先考虑加乘。显然信息就是区间和。那么标记可以只维护单单一个增量吗?难以复合。因为加乘会相互影响,具体的:\(((a\times b + c)\times d+e=bd\cdot a+cd+e\)。所以我们至少要维护两个量,并规定两者的先后顺序。我们规定标记为 \((a,b)\) 表示所有数先整体乘 \(a\) 再加 \(b\)。那么上面可以写成 \((b,c)\times(d,e)=(bd,cd+e)\)。看出来了吗?系数就是两个乘法标记相乘,而加法标记也可以直接写出。

为什么是先乘后加?\((a+b)\times c+d\) 会被认为 \((a+b+d)\times c\),如果要等价就要写成 \((a+b+\frac{d}{c})\times c\)。涉及除法影响精度。

再考虑覆盖操作,只用一个标记 \(c\) 表示覆盖的数即可。一般认为先加乘再覆盖,所以最终的标记就是 \((a,b,c)\) 表示先乘再加最后覆盖。写成代码:

tag merge(tag x, tag y) { //b->a
 	if(x.c) { //如果前面有覆盖操作,那么整个操作就可以看成复合
 		x.c = x.c * y.a + y.b;	
	} else {
		x.a *= y.a;
		x.b = x.b * y.a + y.b;
	}
	if(y.c) x.c = y.c; //如果后面有覆盖,最后就覆盖了
}

2.4 矩阵

如果能够将信息看作向量,每一个修改看作一个矩阵,那么用线段树直接维护矩阵是第一选择。为什么可以这样做?因为矩阵天然满足结合律,所以所有标记的复合就是矩阵乘法,不需要思考。

普通的矩阵是 \((\times,+)\) 矩阵,广义矩阵可以是 \((+,\max)\) 同样有结合律。而广义矩阵通常用在维护最值问题中。矩阵直接用一般来说常数会很大,是因为暴力矩乘的原因,但它本质上就是维护一堆标记。所以找到矩乘中冗余的转移,手动做矩乘不会超时了。

所以说矩阵是方便我们推式子和理解,写的时候不一定要直接写。

例题:P4314 CPU 监控[NOIP2022] 比赛

3 区间最值相关

一个例题:

  1. 区间取最小值
  2. 区间加
  3. 求区间和

这类问题的特点就是修改操作有区间取最值,定位到的区间无法快速统计信息。考虑维护区间最大值 \(mx\) 与严格次小值 \(se\)。然后对于 \(1\) 操作 \(x\) 分类讨论:

  1. 如果 \(mx\le x\),那么没有任何影响,退出。
  2. 如果 \(se<mx<x\),那么唯一修改的就是所有最大值,此时维护一下区间最大值数量 \(ct\) 就可以快速维护总和。
  3. 否则继续向下递归。

可以证明这样的复杂度是 \(O(n\log^2 n)\) 的。

这样做,标记怎么构造?我们将数分为最大值和非最大值,每一部分有不同的增量,分别维护即可。具体的,维护 \((a,b)\) 表示最大值的增量与非最大值的增量,下传时注意最大值的增量只能下传给两个儿子的最大值(相同则一起下传),如果不是最大值就下传非最大值的增量。

void pd(int u) {
	int mx = std::max(t[u << 1].mx, t[u << 1 | 1].mx);
	if(mx == t[u << 1].mx) {
		mdf(u << 1, add1[u], add2[u]);
	} else mdf(u << 1, add1[u], add2[u]);
	if(mx == t[u << 1].mx) {
		mdf(u << 1 | 1, add1[u], add2[u]);
	} else mdf(u << 1 | 1, add1[u], add2[u]);
}

如果询问多一个区间最大值,因为操作 \(1\) 定位到的区间最值一定会修改成 \(x\),子节点也是 \(x\),并且区间加并不影响该区间最值,所以也容易快速维护。

这里包含最值操作的主要思想就是将最值操作转变为了加减操作,是我们熟悉的。

4 历史版本问题

每次询问不止问当前的信息,还需要询问历史信息相关。可以看作多了一个数组 \(B\) 需要维护。

通常维护的历史版本信息有历史最大值、历史最小值、历史版本和。

而历史最值的维护方法一般要维护最大/小版本增量标记。

4.1 不含最值操作

询问是历史最大值、最小值,可以用普通线段树维护,如 P4314 CPU 监控

对于修改是区间加减,求历史最大值的和、历史最小值的和、历史版本和。这里有三种转化方法。

4.1.1 历史最大值

定义数组 \(C_i=A_i-B_i\),此时 \(C_i\) 显然是非正的,一次区间加减操作对 \(C\) 的影响便是 \(\min(A_i+x,0)\)。

那么维护 \(C\) 数组就是上文提到的问题了,吉司机线段树实现,复杂度 \(O(n\log^2n)\)。

4.1.2 历史最小值

同理,定义数组 \(C_i=A_i-B_i\),此时 \(C_i\) 显然是非负的,一次区间加减操作对 \(C\) 的影响便是 \(\max(A_i+x,0)\)。

复杂度 \(O(n\log ^2 n)\)。

4.1.2 历史版本和

这个根本不涉及最值操作,直接打标记就可以做了。复杂度 \(O(n\log n)\)。

4.2 含有区间最值操作

如果含有区间最值操作,就用吉司机线段树将最值操作化为区间加减问题,就可以打 tag 维护了。

标签:浅谈,标记,线段,矩阵,区间,最大值,最值
From: https://www.cnblogs.com/FireRaku/p/18561026

相关文章

  • 树状数组 Color the ball hdu 1556 线段树 洛谷p3372
    目录前言树状数组  lowbit函数  直观表述    代码   运行结果树状数组构建代码树状数组的应用  单点修改和(单点)区间查询  结合差分数组区间修改,单点查询        差分数组Colortheballhdu1556  问题描述  问题分析......
  • 基于木舟平台浅谈surging 的热点KEY的解决方法
     一、概述     上篇文章介绍了基于surging的木舟平台如何构建起微服务,那么此篇文章将介绍基于木舟平台浅谈surging的热点KEY的解决方法     木舟(Kayak)是什么?      木舟(Kayak)是基于.NET6.0软件环境下的surging微服务引擎进行开发的,平台包含了微......
  • 【学习笔记】线段树合并 & 分裂
    【学习笔记】线段树合并&分裂前置知识:动态开点线段树用来解决一些对区间拆分合并的问题。线段树合并大概可以替代DSU,但是常数略大。对于线段树分裂合并的空间复杂度问题,一般内存要开\(maxq\timest\times\lceil\log_2maxn\rceil\),其中\(maxq\)为询问次数,\(t\)为每......
  • .NET Core 特性(Attribute)底层原理浅谈
    简介烂大街的资料不再赘述,简单来说就是给代码看的注释Attribute的使用场景Attribute不仅仅局限于C#中,在整个.NET框架中都提供了非常大的拓展点,任何地方都有Attribute的影子编译器层比如Obsolete,ConditionalC#层GET,POST,Max,Range,RequireCLRVM层StructLayout,DllImp......
  • 浅谈网络文件系统原理
    本文分享自天翼云开发者社区《浅谈网络文件系统原理》,作者:谢****云什么是网络文件系统?网络文件系统(NetworkFileSystem,NFS)实现了一种软件协议,能将远端的文件系统映射到本地,使用者访问网络上的文件就像在使用自己的计算机一样。远端是专属存储系统,通常称为NAS存储。比较出名的......
  • 网络-浅谈批量通讯和自主通讯的区别
    批量通信和自动通信是两种不同的通信概念,它们在多个方面存在显著的差异。以下是对这两种通信方式的详细解释:一、批量通信定义:批量通信通常指的是在数据通信过程中,将大量的数据或信息作为一个整体或批次进行传输。特点:高效性:由于数据是以整体或批次的形式进行传输,因此可以显著......
  • 线段树优化DP
    dp,即动态规划中有一类很重要的优化,叫做线段树优化。本文将介绍几种常见的类型及其套路和一些例题。前置知识:线性dp、线段树。权值线段树优化dp此类题目的dp转移通常和数值的大小关系有关,以下将介绍几道权值线段树优化DP题。CF597CSubsequences给定一个\(1\simn......
  • TYPE-C PD浅谈(四)
    TYPE-CPD浅谈(四)当对接识别完成后,Provider会先在VBUS上提供5V,接着会在CC脚位上送出SourceCapability(SRC_CAP),格式如下:内容定义了供电的各种选项,如共有几组电源可选,相对应的电压电流等。当Consumer接收到SRC_CAP封包后,会针对电源列表的内容,挑选一组电压,再发出需求指令给Provid......
  • 浅谈阅读
    似乎很小我就爱上阅读了。这份热爱如同种子般,在童年的心田里悄然生根发芽。每当翻开书页,如同推开了一扇通往新世界的大门,那里有奇幻的世界、深邃的哲学思考,还有无数令人着迷的故事和知识。阅读,成了我童年最宝贵的伴侣,它陪伴我度过了无数个静谧的午后和星光璀璨的夜晚。初三的日......
  • 鸿蒙NEXT开发教程:浅谈@ComponentV2装饰器
    听说今天的广州车展上有一部分人已经看到华为汽车的最后一“界”,尊界超豪华大轿车,应该很快就要正式亮相,可以期待一波。在api12之后,鸿蒙系统推出一个V2版本的状态管理装饰器,不过目前还在开发试用状态,幽蓝君仔细研究了一下,今天跟大家做一个简单的介绍。幽蓝君对V2版本装饰器的总结......