【数据结构模型整理】
矩形chkmax,单点求值
先考虑一个简单的情况,矩形为3-side矩形,假设这些矩形都贴着右边界,那么我们可以从左往右扫描线,每遇到一个矩形的左边界,就在线段树上区间chkmax,扫到对应的单点时,就进行查询。用一个标记永久化的线段树+扫描线很容易维护。
考虑4-side 矩形的情况,发现我们很难通过扫描线加一维数据结构进行维护,因为chkmax不支持差分,也不能进行撤销操作,那么我们沿用刚才3-side矩形的思路,考虑对每个矩形找一条线劈开,变成两个3-side矩形。
思考如下分治做法:
进行中点分治,对于所有完全包含在当前区间内的且跨过中点的矩形,发现分治时取的中点,恰把这些矩形切成了两个3-side矩形,把这些矩形扫描线,同时把在当前区间里的查询点的答案进行更新。然后往左右递归成两个子问题即可。
上述做法的正确性应该不难发现。分析一下它的复杂度:
对于每个矩形我们只会在第一次被区间中点划开时计算,因此矩形修改的总复杂度是一个log,对于单点查询每个点最多被log个区间覆盖,每次查询的复杂度是一个log,因此查询的总复杂度是两个log。
总复杂度:
具体实现可以把矩形用vector记到它的左端点上,那么每个矩形被扫过的总次数是1个log,不影响复杂度。查询点同理,用vector记下来,比较方便。
二维数点
题意:
在一个二维平面内,给定n个点,每次询问一个矩形中的点数。
- 树状数组:
适用范围:静态,可离线
做法:可以预先把询问差分成两个1-side矩形,再把这n个点按横坐标排序,从左往右扫描每个点,树状数组维护对应纵坐标的点数。
时间复杂度:
空间复杂度:
常数:小
代码难度:小 - 主席树:
适用范围:静态,支持在线
做法:先对n个点按横坐标排序,依次加入到主席树中,对于每个询问同样差分成两个1-side矩形。
时间复杂度:
空间复杂度:
常数:中
代码难度:小 - 树套树:
适用范围:支持单点修改,可在线
做法:数点满足可差分性,可以用树状数组套动态开点线段树或BIT套平衡树。
做法:树状数组维护一个维度,若是套动开线段树,则线段树下标x维护纵坐标为x,横坐标在这棵线段树所在树状数组的区间内的点数。单点修转化为插入和删除,trival。(用set没法做哈,想要一个log的空间必须写平衡树)
时间复杂度:
空间复杂度:前者,后者
常数:大
代码难度:大
区间颜色种数
题意:给定一个序列,每个点有一个颜色,每次询问一个区间,求区间颜色种数。
- 莫队
莫队擅长维护区间中每种数的出现次数这样的信息。显然左右端点加1减1很容易维护颜色数,因此可离线的话,可以解决。
但莫队太无脑了,以下都是一个log的做法 - 考虑离线下来扫描线枚举右端点,时刻维护每种颜色最后出现的位置,在这个位置值为1,其余为0,查询即为对应区间的和,BIT维护即可。
- 考虑区间颜色的一个常见套路,先算出每个点的lst[i]表示,位置i的颜色上一次出现的位置。我们扫描线枚举右端点,用数据结构维护每个左端点的值,容易发现其实变成区间加,单点求值,BIT维护即可。
- 实际上可以转化为二维数点模型,区间[l,r]中lst<l的点的个数,用上面总结的方法求解。
区间众数
维护区间gcd
用线段树维护,复杂度为,可以用势能分析证明
询问区间中所有子区间的信息
- 询问和
转成一阶,二阶,三阶前缀和即可 - 更通用的做法
把询问区间离线下来,按右端点排序,对序列维扫描线,扫描到一个询问区间的右端点时,进行查询,扫描线的过程中,用数据结构维护出每个的信息,下标i存储以i为左端点,右端点属于[i,r]的所有区间的信息(也可以理解为如果对每个i维护出区间[i,r]的值,那么实际上就是要维护i的历史版本和)。
如果信息是可累加的,那么对于询问区间[l,r],只需在扫描线扫到r处时,查询[l,r]的区间历史版本和即可。
例题
按照套路扫描线,那么可以用线段树维护出每个i,区间[i,r]的状态(只有0/1),修改都是对一个后缀异或1,那么维护每个点的历史版本和即可。
有两种做法:
一个是用线段树维护val,每次操作即:区间01翻转,全局对值为1的数的val++
二是用两颗平衡树维护,一颗存值为0的数,一颗存值为1的数,后缀异或即把两颗平衡树的后缀交换,然后对存值为1的数的平衡树全局+1即可。
矩形加,矩形求和
终于又更新了!
题目转送门
- 二维树状数组
适用范围: 范围较小,可以开下大小的数组
设矩形以(a,b)为左上角,(c,d)为右下角
先差分,对于一次矩形+val,差分成add(a,b,val),add(c+1,d+1,val),add(c+1,b,-val),add(a,d+1,-val);
那么对一个点(i,j)进行查询得到的结果,就是这个点的单点值,但我们想求的是二维前缀和
推一下式子:
我们仿照一维树状数组区间加区间求和的方法,把式子拆成几项,分别用树状数组来维护,因为,所以分别维护即可。
优点:,常数小,很容易写,支持在线。
缺点:空间,当较大时不适用
提交记录 - cdq分治+历史版本和线段树
适用范围:可离线
这是一个动态问题,而且满足每个查询只与他前面的修改有关,且修改可以分批次地贡献到查询,那么用cdq分治,可以用一个log代价转化为静态版本:
即二维平面里有一堆矩形,我们希望询问一个矩形区域的和。
先用扫描线降下一维,矩形和,和矩形加都转化为差分操作,那么可以用历史和线段树维护。
一些细节:要添加一个清空标记,因为每次cdq分治后都要清空。在相邻两个查询或修改中要更新历史和几次要想清楚。
时间复杂度同上,但是常数大一些,优点是空间是线性了。
提交记录 - cdq分治+树状数组
首先还是cdq分治,但是对于这个静态版本,我们没有必要用复杂的历史版本和线段树,其实可以仿照二维树状数组的手法,拆成四个树状数组,但是由于扫描线扫掉了一维,因此只需开一维就够了,空间复杂度为线性,而且比历史和线段树好些许多,常数也比线段树小。 - 二维线段树
是的,树套树也能支持矩形修改。方法就是标记永久化,外层树每个节点存两颗线段树,一颗是val,一颗是lazy,在外层树上划分区间时,对于完全包含的区间还要额外在lazy上进行修改,对于路径上的区间只在val上修改,还要乘这个节点所表示的区间和修改区间的交作为系数。查询时,对于路径上的区间要乘上查询区间和当前区间的交作为系数,对于完整包含的区间直接查询累计答案即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】