算法学习笔记(44): 二维问题小计

首先需要理解什么是二维问题。

n 维空间体系:将元素变成 n 维空间中的点,将范围变成 n 维空间中的正交范围。

二维问题就是每一个元素都可以看作一个平面上的坐标 (x,y)。其中一维可以是下标,时间,值,dfn,甚至是一个函数 (x,f(x))

经典的二维问题实际上就是矩形加,矩形取 max,单点查,线查,矩形查。

而二维问题一般是通过某种建模方式变成上述操作进行操作。


  • Δ 区间一次问题

例如给定序列,多次询问一个区间是否有重复的数。

虽然这原本是一个一维问题,但是可以升维更简单的处理。

  • 解法 1:经典方法,令 prei 表示 i 前面第一个与它相同的位置,那么一个区间没有重复的数当且仅当 maxi=lrprei<l,于是升维之后变成了一个简单的区间 max 问题。
  • 解法 2:将每一种数单独处理,设 fx(i) 表示值为 x 的第 i 个数的位置,那么一个区间 [l,r] 如果只被 x 覆盖了一次,那么需要满足 l(fx(i),fx(i+1)]r[fx(i+1),fx(i+2))。将询问看作二维平面上的一个点,那么被贡献到的矩形有 O(n) 个,而一个区间全部都只出现了一次,那么它一定被贡献的区间长度次,于是问题转化为矩形加,单点查的问题,只是需要离线,并且常数略大。
  • 解法 3:类同解法 2,只是考虑被 x 覆盖了不止一次的区间,那么需要满足 lfx(i)rfx(i+1),这样的矩形也是 O(n) 个的。
  • ……或许还有更多的想法

对于解法 1 可以看作一个很好的套路,被玩烂了,但是解法 2/3 就非常有意思。

我们首先将每一个点对于区间的贡献拆了出来,也就是拆贡献。将自由度为二的问题看作了平面上的点,拆除的贡献是一个矩形贡献,那么就可以变成经典的二维问题了。

BZOJ#3489. A simple rmq problem[l,r] 之间找到最大的在这个区间里只出现过一次的数。

这用 解法 1 其实也可做,但是如果需要判断一个数是否只是出现了一次,那么不仅需要前驱 prei,还需要后继 posti,当 i[l,r]prei<l 并且 posti>r 的时候才可以对答案做出贡献,这是一个三维偏序问题,利用 cdq 分治可以做到 O(nlog2n) 的复杂度。

利用 解法 2 来做无论是代码还是思路都将十分的简单,因为我们只需要将提及的矩形加贡献变为矩形取 max 贡献即可,利用扫描线与 set 可以做到 O(nlog2n+qlogn) 的复杂度,注意到这个是可以转化为可持久化线段树支持在线的。


  • Δ 换维扫描线

将动态的一维问题转化为静态的二维问题,实际上就是增加了时间这一维度。

这里的换维扫描线实际上就是将对于时间维的扫描线(动态的一维问题)转化为对于序列的扫描线(另一个动态的一维问题)

例如维护一个序列,支持:

  • 区间 +
  • 单点求历史最值

这道题实际上可以利用历史版本最值线段树完成……但是这里不是讲这个的。

历史版本最值线段树可以参考:# 算法学习笔记(34): 矩阵乘法与线段树标记

如果我不会这个神秘的科技怎么办?如果以序列为横坐标,时间为纵坐标:

那么区间修改形如红线,而查询最值形如蓝线

上下略微有些颠倒,不过问题不大。

如果我们竖着来扫描线,那么修改就变成了单点修改,而蓝线也就是前缀查询。

而历史最值呢?实际上也就是一个前缀最值问题,这是容易维护的。

  • 例题UR #19 前进四 维护一个序列 A,单点修改,查询某个后缀后缀最小值的个数。

首先对于每次询问暴力做是简单的,注意到我们只需要维护两个变量:mn,cnt 表示后缀最小值和变化的次数。

每次 mnmin(mn,Ai),如果 mn 改变了,那么 cntcnt+1

我们对于其多加一个下标 t,那么 mntmin(mnt,At,i)

注意到 At,ii 固定的时候有若干连续段的值是相等的,这样的段是 O(q) 的,考虑同统一进行上述转移,其实也就是换维扫描线,那么问题转化为维护一个序列:

  • 区间取 min
  • 单点查询一个点被取 min 变小的次数

这是 seg beats 需要完成的事,不在本文讨论范畴内。

还有一些利用这个方法完成的题:


我们说了序列上的二维问题,对于树上的二维问题又是什么呢?

  • Δ 树上二维问题

一般来说,我们需要利用 dfn 序的映射,因为它具有很好的性质。

  • :定义一个点对 (i,j) 是好的当且仅当:kN,ik=j。给定一棵 n 个点的树,树上点的权值互不相同,每次询问给定一个路径 (x,y),问路径上多少个点对 (u,v) 满足 uv(au,av) 是好的。1n,q,V5×104

对于每一个 x 可以 O(m) 预处理出可以与那些点形成贡献,考虑这些点对会对那些路径造成贡献。

  • 点对非祖孙关系,那么如果路径两个端点分别在点对的子树内,那么可以被贡献。
  • 点对为祖孙关系,若 x 是浅的那个,那么一个端点 dfnx 或者 dfnx+sizx,另一个端点 dfny<dfny+sizy

发现将左右端点看作二维平面上的点,那么一个点对会导致 O(1) 个矩形加法,于是扫描线即可,可持久化之后可以强制在线。

  • :用 n 个节点建出了了两棵有根树,询问两棵树某两个子树内的节点是否有交。

同一个节点在两个树上的 dfn 是不同的,所以自然是一个二维的元素。

由于子树内的 dfn 是一段连续的区间,那么问题转化为判定一个矩形内是否存在一个点,那么也就是经典的二维数点问题,随便做啦。

这个问题实际上是 [IOI2018] werewolf 狼人 的后半部分,前一半部分是 kruskal 重构树,可见:# 算法学习笔记(30):Kruskal 重构树

一道强化的问题,带修:Intersection of Permutations


所以讲来讲去,二维问题到底是怎样的一个问题,有没有什么通法来解决呢?个人理解,仅供参考

二维问题,大部分情况下是表面上的一维问题。但是一维能够维护的信息太少太少了,所以需要更多的信息,例如时间,数值来增加可以维护的信息量,从而更好的解决问题。

而通法……我太菜了,没有通法

posted @   jeefy  阅读(198)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示