处理多维关系汇总

什么是多维关系

多维关系基本上指偏序关系,即一个元素包含多个属性\((a,b,..)\),每次需要快速查找满足某一关系的所有元素,再进行整体处理(权值和,第\(k\)大...),如最基本的逆序对,每一个元素包含了位置和权值\((pos,val)\),对于每一个\(i\),需要查询的是满足\(pos_j<pos_i\land a_j>a_i\)\(j\)的个数。

在处理这类问题的时候,首先需要将题目包含的关系找到,再运用各种方法,不断消除偏序关系,最终使满足条件的元素在一个可以整体处理的数据结构中,就可以很好的解决了。

下面就将介绍一些消除偏序关系的方法。

排序

没错,最简单的方法就是排序。

设偏序关系是\(a_i<a_j\)(下同),那我们就按\(a_i\)降序排序,这样当处理\(i\)的时候,所有\(a_j>a_i\)\(j\)就已经被处理好了,这时候我们就消去了\(a\)这一偏序关系。

当然,如果偏序关系是\(a_{j,1}<a_{i,2}<a_{j,3}\)(保证\(a_{i,1}\le a_{i,2}\le a_{i,3}\))的时候,我们按\(a_{i,2}\)排序后,可以满足\(a_{j,1}<a_{i,2}\),但\(a_{i,2}<a_{j,3}\)的怎么满足呢?其实只要在删除\(a_{j,3}<a_{i,2}\)\(j\)就可以了。或许你已经听过这个思想,它还有另外一个名字——扫描线。

排序可以消除一维关系,剩下一维整体处理,但往往题目中的偏序关系不止两维,这时候就需要更高级的方法了。

KDT

KDT是用来处理在\(k\)维空间上,维护单点的二叉树/线段树结构。核心是将点进行不断划分,使整颗树的深度为\(O(\log n)\)级别,对一个矩形查询/修改一次的复杂度为\(O(n^{1-\frac{1}{k}})\)

建树:选择方差最大的一个维度(注意方差计算时开double),然后以该维度上的中位数划分当前区间,即将小于(等于)的放在左边,大于(等于)的放右边。然后递归处理。pushup的时候维护包含所有点的最小矩形的边界。

查询/修改:正常修改,即当前矩形范围被询问矩形包含即操作,返回,完全不包含的情况就返回。然后由于某种奇妙的性质,整个过程的复杂度为\(O(n^{1-\frac{1}{k}})\)

两种不同的结构

  • 线段树式:只有叶子节点才表示一个节点,空间4倍。

    ​ 优点:处理方便,pushup时不需要考虑边界,pushdown也少一些细节。

  • 二叉树式:每一个节点就是一个节点,空间1倍。

    ​ 优点:可以方便的支持动态加点(需要判断若某一个儿子的节点大小大于整颗子树大小的\(0.6\)倍时重构)。

特点:

  • 空间\(O(n)\),但时间\(O(n\sqrt n)\),如果查询的点动态插入的话需要\(O(n\sqrt {n\log n})\)(插入\(\sqrt n\)的点重构整颗树),或\(O(n^{0.57})\)的子树重构。但是建好了的点的属性是不能改变的!

    所以一般在已知要查询的点时可以使用KDT,需要修改点的属性的时候不能使用KDT。

  • 由于其和线段树一模一样的结构,可以完全支持线段树的所有处理办法,如吉老师线段树。

    所以一般在整体处理复杂但有结合律(这是所有可以拿标记维护的基础)的时候可以使用KDT。

  • 复杂度和值域无关!

树套树

树套树,即在树型数据结构的每一个节点上再开一个树型数据结构。在第一个数据结构的处理中删掉第一个偏序关系,而第二个数据结构进行整体处理。

先说明一下内层与外层的最主要的区别:我们需要在多个独立的内层数据结构中得到结果并合并起来,所以不能处理第\(k\)大之类的问题。所以一定要选择好先消除哪个偏序关系,留下哪个做处理。

  • 树状数组

    • 内层

      树状数组放入内层的时候无法动态开点,用unordered_map常数巨大,所以无法做内层

    • 外层

      可以单点加、区间查,或区间加单点查,无法处理区间加,区间查。

      只能求前缀min/max,不能求区间min/max

      常数很小。

      • 线段树(值域线段树)
    • 内层

      可以处理所有单点,区间问题,在处理区间问题时注意标记永久化,可以极大减小常数。

      在有重点的情况下,处理比较困难。

      值域限制,而且空间为\(O(n\log^2 n)\)

    • 外层

      和内层基本一致,处理此处标记无法下传,所以必须标记永久化。

  • 平衡树

    • 内层

      处理无值域限制,空间为\(O(n\log n)\)

      常数大

    • 外层

      好像要替罪羊树,没写过,唯一能多干的事就是支持动态插入。

树套树与KDT的比较

  • 常数方面:最经典的动态二维数点,随机加入和查询\(10^5\)次,运行结果如下:

    值域\(10^5\) 值域\(10^9\)
    子树重构kdt \(1.45s\) \(1.45s\)
    根号整树重构kdt \(1.65s\) \(1.65s\)
    树状数组套线段树 \(0.75s\) 受值域限制
    树状数组套平衡树 \(1.89s\) \(2.5s\)
  • 处理问题方面:

    • kdt处理两维限制是一起消除的,所以无法处理某些只关系一维的问题,如第\(k\)大。
    • 而树套树内层的每个点实际上有多种地方出现(即在外层的多个节点出现),那些需要前后关联的操作就无法实现,如区间赋值。
  • 实际问题

    • CF44G Shooting Gallery

      二维平面,给定一些点,点带点权,每次操作查询矩形内最小权值的点,并删除。

      首先查询是最小值,外层不能用树状数组,内层线段树处理困难,平衡树常数巨大,所以推荐使用kdt。

    • P1975 [国家集训队]排队

      每次交换数列中两个数,查询整体逆序对数。

      分析一下得:求数列在\((x,y)\)而且值在\((l,r)\)的数的个数,然后单点修改值

      虽然是明显的二维数点,但涉及第二维值的修改,即需要维护删点和加点,难搞。不如直接树状数组套线段树。

    • P6349 [PA2011]Kangaroos

      给定每个元素是一个区间的序列,多次询问,给出 A,B,求出 a 中最长的区间(即这个序列中的一段),使得这个区间内每个区间都与$ [A,B]$有交集。

      分析一下得:二维平面,有一些点,每次给一个矩形,使包含在矩形内的点权值+1,其余清零,求每个点的历史最值。

      又是二维点的处理,但这里是要加、赋值、历史最值,赋值就根本无法处理,历史最值更没得搞。只能用kdt。

cdq分治

cdq分治是将动态(在线)的问题转化为静态(离线)的问题。

怎么理解这句话?

就好比这样一个问题:

有一个数组,数轴上有一些点,多次询问,每次询问给定一个区间,求区间所包含的点数。

如果提前告诉你所有的点,然后再去询问。这时候你知道,所有的点都可能会在区间中,就可以将点按坐标排序,在区间左端点后、右端点前的就在区间内。

但如果在询问之后又加了一些点,并且后面还有询问...这时候上面的办法就没用了,因为在询问后加入的点不可能前面的询问矩形内。这时候你按坐标排序,在区间左端点后、右端点前的点不一定在区间内。

上面这两种情况就分别对应了静态和动态(你也可以理解为动态情况多了时间轴的偏序关系,即只有时间前的才能贡献时间后的,这也是为什么我将cdq放在了这个专题里)

  • 算法流程:

    套用上面的问题,有一个时间先后的操作序列,\(\text{C}\)为加点,\(\text{Q}\)为查询。如\(\{C\;\;C\;\;Q\;\;C\;\;Q\;\;C\;\;Q\;\;C\}\)

    考虑从正中间划分\(\{C\;\;C\;\;Q\;\;C\;|\;Q\;\;C\;\;Q\;\;C\}\)

    这时候你发现,在分割线左边的加点操作一定先与右边的查询操作\(\{C\;\;C\;\;.\;\;C\;|\;Q\;\;.\;\;Q\;\;.\}\)

    那么这就转化为了静态的问题,在给左边的\(\text{C}\)和右边的\(\text{Q}\)打上标记之后,就可以按照上面引入部分的方法解决了(处理的时候我们只处理带标记的加点和查询)。

    处理完之后再去递归处理分割线左边的\(\{C\;\;C\;\;Q\;\;C\}\)和右边的\(\{Q\;\;C\;\;Q\;\;C\}\)

    当然,你可能会说排序之后分割线左右的已经不是原来那样了,没有关系,我们先递归再处理也是可以的。

  • 不一样的cdq

    上面所介绍的是最基本的cdq处理问题方法,其实还有很多情况是cdq可以处理的,下面是一些cdq的技巧(或许是)

    • 偏序降维\(\rightarrow\)时间轴 (P3810【模板】三维偏序

      我们可以将一个静态的问题,将某一偏序关系排序之后,就变成了"只有前面才会贡献后面"的动态问题。

    • 查询与修改的相互转化(P3810【模板】三维偏序

      这题中,我们没有修改,而查询满足条件的点对数。看似与上面我们介绍的修改和查询分开不同,但实际我们在处理的时候,就可以把分割线左边的点当做修改,右边的点当做查询。这样在查询到的点全是左边的点,满足题面的要求。

    • 优化带依赖关系的转移 (P3769 [CH弱省胡策R2]TATT

      有这样的一个DP:\(dp_{i}=\max\{a_i\ge a_j\land b_i\ge b_j\land c_i\ge c_j\land d_i\ge d_j|\;dp_{j}\}+1\)

      同样,先排序消除一维偏序关系,转化为动态问题cdq分治后,发现这个dp转移好像又有所不同,其实我们可以将左边点当做修改(加入一个值为\(dp_i\)的点),右边的查询满足条件的最大值。

      注意:由于一个点在转移时必须先知道其dp值,所以遍历分治树时必须按中序遍历,并且为了避免破坏原来的顺序,需要额外开一个数组去处理。

    • cdq嵌套(P3769 [CH弱省胡策R2]TATT

      在消除第一维偏序之后,其实还可以继续cdq消除第二维偏序,此时与三维偏序不同的是这里我们已经明确了修改和查询。

    • 容斥计算答案(CF1045G AI robots

      当询问的偏序关系是\(a_i-k\le b_j\le a_i+k\)的时候,而我们内层为了让常数小而使用树状数组的时候,我们可以考虑将询问拆成两个,即加上\(\le a_i+k\)的答案,减去\(\le a_i-k-1\)的答案。

  • 其他:

    • 关于cdq与树套树和kdt的比较

      cdq常数吊打另外两个!

      cdq的处理方法类似树套树,在消除偏序关系的时候分割了修改,所以无法处理如区间赋值,区间加的操作。

      cdq必须离线。

    • 复杂度:

      分治层数为\(O(\log n)\)层,看处理一层的复杂度是多少。一般是\(O(n\log^2n)\)

      优化:如果处理一层只需要\(O(len)\),而需要排序,并且处理的问题非依赖转移类,可以使用归并排序优化到\(O(n\log n)\)

常见的偏序关系

排除最明显的\(a_i<a_j\)

  • 区间关系(P5445 [APIO2019]路灯

    \(\text{区间[l_1,r_1]包含区间[l_2,r_2]}\rightarrow l_1\le l_2\land r_2\le r_2\)

    \(\text{区间[l_1,r_1]与[l_2,r_2]相交}\rightarrow l_1\le r_2\lor l_2\le r_2\)

  • 树上路径关系(P3242 [HNOI2015] 接水果

    路径A包含路径B\(\rightarrow\)dfs序的关系(分\(lca_{x,y}=x\)和不等两种关系)

  • 绝对值(P4169 [Violet]天使玩偶/SJY摆棋子

    \(a_i\)\(a_j\)的大小确定了,那么\(|a_i-a_j|\)的正负也确定了。

    所以我们可以先处理有\(a_i\le a_j\)关系的,再处理有\(a_i> a_j\)关系的。

  • 最小值最大值(CF1045G AI robots

    \((x_i-x_j)\leq\min(r_i,r_j)\),我们可以以\(i\)去考虑所有\(r_i<r_j\)\(j\),这样就可以将min去掉。

若遇到更多的应该会添加。

总结

​ 做这类题的大概步骤就是:找到题目里的偏序关系\(\rightarrow\)选用合适的算法\(\rightarrow\)一定要对拍!

posted @ 2022-02-17 19:31  qwq_123  阅读(140)  评论(0编辑  收藏  举报