【数据结构模型整理】

矩形chkmax,单点求值

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


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


上述做法的正确性应该不难发现。分析一下它的复杂度:
对于每个矩形我们只会在第一次被区间中点划开时计算,因此矩形修改的总复杂度是一个log,对于单点查询每个点最多被log个区间覆盖,每次查询的复杂度是一个log,因此查询的总复杂度是两个log。
总复杂度:\(O(q\log^2n)\)
具体实现可以把矩形用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(N\log^2N)\)
    空间复杂度:前者\(O(N\log^2N)\),后者\(O(N\log N)\)
    常数:大
    代码难度:大

区间颜色种数

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

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

区间众数

维护区间gcd

用线段树维护,复杂度为\(O(\log N+\log V)\),可以用势能分析证明

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

  1. 询问和
    转成一阶,二阶,三阶前缀和即可
  2. 更通用的做法
    把询问区间离线下来,按右端点排序,对序列维扫描线,扫描到一个询问区间的右端点\(r\)时,进行查询,扫描线的过程中,用数据结构维护出每个\(i\in [1,r-1]\)的信息,下标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(n^2)\)大小的数组
    设矩形以(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)进行查询得到的结果,就是这个点的单点值,但我们想求的是二维前缀和\(s_{i,j}\)
    推一下式子:
    \(\begin{aligned}s_{i,j} &=\sum_{x=1}^i\sum_{y=1}^ja_{x,y}\\&=\sum_{x=1}^i\sum_{y=1}^j\sum_{u=1}^x\sum_{v=1}^yd_{u,v}\\&=\sum_{u=1}^i\sum_{v=1}^jd_{u,v}\sum_{x=u}^i\sum_{y=v}^j1\\&=\sum_{u=1}^i\sum_{v=1}^jd_{u,v}\times (i-u+1)(j-v+1)\end{aligned}\)
    我们仿照一维树状数组区间加区间求和的方法,把式子拆成几项,分别用树状数组来维护,因为\((i-u+1)(j-v+1)=(i+1)(j+1)-(i+1)u-(j+1)v+uv\),所以分别维护\(d_{u,v},d_{u,v}\times u,d_{u,v}\times v,d_{u,v}\times uv\)即可。
    优点:\(O(q\log^2 n)\),常数小,很容易写,支持在线。
    缺点:空间\(O(n^2)\),当\(n\)较大时不适用
    提交记录
  2. cdq分治+历史版本和线段树
    适用范围:可离线
    这是一个动态问题,而且满足每个查询只与他前面的修改有关,且修改可以分批次地贡献到查询,那么用cdq分治,可以用一个log代价转化为静态版本:
    即二维平面里有一堆矩形,我们希望询问一个矩形区域的和。
    先用扫描线降下一维,矩形和,和矩形加都转化为差分操作,那么可以用历史和线段树维护。
    一些细节:要添加一个清空标记,因为每次cdq分治后都要清空。在相邻两个查询或修改中要更新历史和几次要想清楚。
    时间复杂度同上,但是常数大一些,优点是空间是线性了。
    提交记录
  3. cdq分治+树状数组
    首先还是cdq分治,但是对于这个静态版本,我们没有必要用复杂的历史版本和线段树,其实可以仿照二维树状数组的手法,拆成四个树状数组,但是由于扫描线扫掉了一维,因此只需开一维就够了,空间复杂度为线性,而且比历史和线段树好些许多,常数也比线段树小。
  4. 二维线段树
    是的,树套树也能支持矩形修改。方法就是标记永久化,外层树每个节点存两颗线段树,一颗是val,一颗是lazy,在外层树上划分区间时,对于完全包含的区间还要额外在lazy上进行修改,对于路径上的区间只在val上修改,还要乘这个节点所表示的区间和修改区间的交作为系数。查询时,对于路径上的区间要乘上查询区间和当前区间的交作为系数,对于完整包含的区间直接查询累计答案即可。
posted @ 2022-05-31 21:46  glq_C  阅读(237)  评论(0)    收藏  举报