【数据结构模型整理】

矩形chkmax,单点求值

先考虑一个简单的情况,矩形为3-side矩形,假设这些矩形都贴着右边界,那么我们可以从左往右扫描线,每遇到一个矩形的左边界,就在线段树上区间chkmax,扫到对应的单点时,就进行查询。用一个标记永久化的线段树+扫描线很容易维护。
考虑4-side 矩形的情况,发现我们很难通过扫描线加一维数据结构进行维护,因为chkmax不支持差分,也不能进行撤销操作,那么我们沿用刚才3-side矩形的思路,考虑对每个矩形找一条线劈开,变成两个3-side矩形。


思考如下分治做法:
进行中点分治,对于所有完全包含在当前区间内的跨过中点的矩形,发现分治时取的中点,恰把这些矩形切成了两个3-side矩形,把这些矩形扫描线,同时把在当前区间里的查询点的答案进行更新。然后往左右递归成两个子问题即可。


上述做法的正确性应该不难发现。分析一下它的复杂度:
对于每个矩形我们只会在第一次被区间中点划开时计算,因此矩形修改的总复杂度是一个log,对于单点查询每个点最多被log个区间覆盖,每次查询的复杂度是一个log,因此查询的总复杂度是两个log。
总复杂度:O(qlog2n)
具体实现可以把矩形用vector记到它的左端点上,那么每个矩形被扫过的总次数是1个log,不影响复杂度。查询点同理,用vector记下来,比较方便。

二维数点

题意:
在一个二维平面内,给定n个点,每次询问一个矩形中的点数。

  1. 树状数组:
    适用范围:静态,可离线
    做法:可以预先把询问差分成两个1-side矩形,再把这n个点按横坐标排序,从左往右扫描每个点,树状数组维护对应纵坐标的点数。
    时间复杂度:O(NlogN)
    空间复杂度:O(N)
    常数:小
    代码难度:小
  2. 主席树:
    适用范围:静态,支持在线
    做法:先对n个点按横坐标排序,依次加入到主席树中,对于每个询问同样差分成两个1-side矩形。
    时间复杂度:O(NlogN)
    空间复杂度:O(NlogN)
    常数:中
    代码难度:小
  3. 树套树:
    适用范围:支持单点修改,可在线
    做法:数点满足可差分性,可以用树状数组套动态开点线段树或BIT套平衡树。
    做法:树状数组维护一个维度,若是套动开线段树,则线段树下标x维护纵坐标为x,横坐标在这棵线段树所在树状数组的区间内的点数。单点修转化为插入和删除,trival。(用set没法做哈,想要一个log的空间必须写平衡树)
    时间复杂度:O(Nlog2N)
    空间复杂度:前者O(Nlog2N),后者O(NlogN)
    常数:大
    代码难度:大

区间颜色种数

题意:给定一个序列,每个点有一个颜色,每次询问一个区间,求区间颜色种数。

  1. 莫队
    莫队擅长维护区间中每种数的出现次数这样的信息。显然左右端点加1减1很容易维护颜色数,因此可离线的话,可以O(nm+m)解决。
    但莫队太无脑了,以下都是一个log的做法
  2. 考虑离线下来扫描线枚举右端点,时刻维护每种颜色最后出现的位置,在这个位置值为1,其余为0,查询即为对应区间的和,BIT维护即可。
  3. 考虑区间颜色的一个常见套路,先算出每个点的lst[i]表示,位置i的颜色上一次出现的位置。我们扫描线枚举右端点,用数据结构维护每个左端点的值,容易发现其实变成区间加,单点求值,BIT维护即可。
  4. 实际上可以转化为二维数点模型,区间[l,r]中lst<l的点的个数,用上面总结的方法求解。

区间众数

维护区间gcd

用线段树维护,复杂度为O(logN+logV),可以用势能分析证明

询问区间中所有子区间的信息

  1. 询问和
    转成一阶,二阶,三阶前缀和即可
  2. 更通用的做法
    把询问区间离线下来,按右端点排序,对序列维扫描线,扫描到一个询问区间的右端点r时,进行查询,扫描线的过程中,用数据结构维护出每个i[1,r1]的信息,下标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即可。

矩形加,矩形求和

终于又更新了!
题目转送门

  1. 二维树状数组
    适用范围: n范围较小,可以开下O(n2)大小的数组
    设矩形以(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)进行查询得到的结果,就是这个点的单点值,但我们想求的是二维前缀和si,j
    推一下式子:
    si,j=x=1iy=1jax,y=x=1iy=1ju=1xv=1ydu,v=u=1iv=1jdu,vx=uiy=vj1=u=1iv=1jdu,v×(iu+1)(jv+1)
    我们仿照一维树状数组区间加区间求和的方法,把式子拆成几项,分别用树状数组来维护,因为(iu+1)(jv+1)=(i+1)(j+1)(i+1)u(j+1)v+uv,所以分别维护du,v,du,v×u,du,v×v,du,v×uv即可。
    优点:O(qlog2n),常数小,很容易写,支持在线。
    缺点:空间O(n2),当n较大时不适用
    提交记录
  2. cdq分治+历史版本和线段树
    适用范围:可离线
    这是一个动态问题,而且满足每个查询只与他前面的修改有关,且修改可以分批次地贡献到查询,那么用cdq分治,可以用一个log代价转化为静态版本:
    即二维平面里有一堆矩形,我们希望询问一个矩形区域的和。
    先用扫描线降下一维,矩形和,和矩形加都转化为差分操作,那么可以用历史和线段树维护。
    一些细节:要添加一个清空标记,因为每次cdq分治后都要清空。在相邻两个查询或修改中要更新历史和几次要想清楚。
    时间复杂度同上,但是常数大一些,优点是空间是线性了。
    提交记录
  3. cdq分治+树状数组
    首先还是cdq分治,但是对于这个静态版本,我们没有必要用复杂的历史版本和线段树,其实可以仿照二维树状数组的手法,拆成四个树状数组,但是由于扫描线扫掉了一维,因此只需开一维就够了,空间复杂度为线性,而且比历史和线段树好些许多,常数也比线段树小。
  4. 二维线段树
    是的,树套树也能支持矩形修改。方法就是标记永久化,外层树每个节点存两颗线段树,一颗是val,一颗是lazy,在外层树上划分区间时,对于完全包含的区间还要额外在lazy上进行修改,对于路径上的区间只在val上修改,还要乘这个节点所表示的区间和修改区间的交作为系数。查询时,对于路径上的区间要乘上查询区间和当前区间的交作为系数,对于完整包含的区间直接查询累计答案即可。
posted @   glq_C  阅读(177)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示