分治类算法

CDQ分治:

理解:用一个 \(\log\) 的代价去掉一个维度/一层分治可以代替一个数据结构。

应用:三维偏序:第一维排序,第二维分治,第三维数据结构。

细节:分治遍历顺序与数组何时排序 ?

一般三维偏序采取后序遍历,这样可以保证在划分区间前后两段的时候,是依据 cmp1 划分的。下面演示一下其他遍历顺序(要多排序一下,跑得慢了)。

先序遍历

中序遍历

题目: P4169 [Violet] 天使玩偶/SJY摆棋子

三维:时间 \(x\) \(y\)

本题稍有变化要进行四个统计,重点注意本题四个象限分开处理后的写法,以及对于第二维度的处理。由于是四个要分开统计所以要先记录下来统计与修改操作用数组保存再进行第二维的排序。然后调用计算函数四次即可。

P4027 [NOI2007] 货币兑换

不要被题目形式所迷惑。

\(f_i\)= \(\max(A_{i} \times cnt_{a}+B_{i} \times cnt_{b},f_{i-1})\)

\(cnt_{b}=\frac{f_{j}}{R_{j}*A_{j}+B_{j}}\)

因为式子中有\(i\)\(j\) 的乘积项,所以考虑斜率优化。

$cnt_{b} = -\frac{A_{i}}{B_{i}}cnt_{a} + \frac{f_{i}}{B_{i}} $

需要维护一个上凸壳,发现斜率和横坐标均不单调怎么办,可以用 \(CDQ\) 分治解决。第一维选取时间维度,第二维选择横坐标,第三维选取斜率。根据状态转移需要,选择中序遍历。由于加点希望按横坐标,查询希望按斜率,故 $ (l,mid) $ 按照横坐标排序,$ (mid+1,r) $ 按照斜率排序。

P3769 [CH弱省胡策R2] TATT

本题是四维偏序可以采用CDQ套CDQ,第一次CDQ结束后根据分治中点打上标记这样就处理掉一维。然后就可以再来一个CDQ进行三维偏序。统计贡献的时候只有标记为 \(0\) 的才能加入树状数组,只有标记为 \(1\) 的才可以进行答案累加。注意由于状态转移需要,必须中序遍历,同时不能忽视排序的稳定性,对第二维排序的时候仍然要以相同时第一维优先。

整体二分

如果单个询问可以用二分,而操作的贡献是方便减掉的,那么对于多个询问可以使用整体二分。其实就是多个问题一起二分,减少共同部分的计算。

P1527 [国家集训队] 矩阵乘法

这里二维区间 \(k\) 大,于是可以利用二维树状数组解决。

P4602 [CTSC2018] 混合果汁

注意这里的操作不具有可减性,于是需要保留前面对后面的影响。

线段树分治

应用场景:支持动态修改的查询类问题且用于查询的数据结构难以快速删除,或者询问一段时间内的答案。

算法是在在时间-序列的二维矩形进行操作,正常按照时间一个一个做就是等价于对于时间进行扫描线,而我们可以离线利用线段树维护时间轴。

我们将遇到的情况分成
修改操作有生效时间,也就是说修改操作是带撤销的,其实询问有一段时效也可以且不具备可减性。解决方案:利用数据结构的可回退性,所以用到的数据结构必须支持回退,比如要是用并查集的话就只能启发式合并了,不能路径压缩。分治的线段树上修改的是区间。角度:如果是操作撤销类可以 dfs 插入删除,在叶子处进行某一时间点的询问。如果是时间区间的询问且询问可以拆,那就拆开来放到各个节点上去。

实现于是我们可以用线段树维护时间轴,对于每个修改/查询操作我们将其打到线段树的节点上去,然后每进入一个节点就要执行该节点上的修改操作,每退出一个节点回到父亲就要撤回该节点的原先操作。其实大部分时间并不需要真正建立出线段树。具体来说我们直接把询问打到节点上去

P5631 最小mex生成树

先考虑暴力枚举答案 \(x\),将所有权值为 \(x\) 的边删去,查看连通性。

但是发现每次只修改一部分边,其他大部分边都是不变的却每次都要枚举到,可以不可以合并重复操作呢。这就启发我们用 \(solve(l,r)\) 表示边权为 \([l,r]\) 之内的边都不加入。这样在处理 \([l,r]\) 中的边的时候就可以不重复处理 \([0,l-1] \cup [r+1,max+1]\) 中的边了。每次先把右边的边全部加入,然后处理左半边,再撤回,加入左半边的边,处理右半边,再撤回。最后在叶子节点处查询即可。

同时注意细节,本题求的是 $ mex $ ,所以边权需要从 \([0,\max w+1]\) 都要考虑。

P5443 [APIO2019] 桥梁

  1. 分块。考虑两种算法,一个是直接修改,暴力查询连通性。我们会感觉到每次图的联通性每次的反复查询似乎有些重复,本着减少重复计算的原则,可以通过对查询边权升序排序,然后按照边权升序大小依次加入这样只需要维护一个并查集即可。对于没有修改的边,可以直接一边查一边加入,而对于有修改的边则需要每次查询都修改一遍。发现此时修改操作又重复了,如何平衡查询次数与修改次数之间的关系呢,我们可以分块!快外保存修改,块内维护一个并查集每次重新修改。这种方法似乎叫根号重构。

  2. 操作的时间顺序,使得我们想到线段树分治算法。但是可持久化并查集或者克鲁斯卡尔重构树不具有回退性,所以用不了线段树分治,但是可以用 \(KDT\) 分治,其中一维是可持久化。现在还没太掌握,以后再更。

P4585 [FJOI2015] 火星商店问题

本题有时间和区间两个维度同时要求最大异或值。

解法一:最直接的想法就是线段树套字典树,先用线段树解决区间问题,再用字典树解决最大异或值,时间问题直接在字典树节点上打标记即可。

解法二:线段树分治+可持久化字典树。用线段树分治处理时间序,用可持久化字典树完成区间异或最大值。

其实综合解法一二会有一个疑惑就是为什么不能可持久化字典树加时间标记呢?原因是用可持久化处理区间信息一般要有可减性(毕竟是 \(val(r)-val(l-1)\) 凑出来的区间嘛),而时间标记不具备可减性!

CF603E Pastoral Oddities

启发式分裂

P4755 Beautiful Pair

分治处理区间 \((l,r)\) 发现肯定是区间中的点跨过最大值点对区间另一边的点产生贡献。 然后在以最大值点为划分分别计算两边答案。但是题目极端数据可能让最大值点的分布不均匀,导致时间复杂度退化。于是我们可以在每次选择最大值点后,扫描较段的一边,较长的有一边用数据结构查询。此时可以在线主席树,也可以离线树状数组。

其他应用

P1429 平面最近点对(加强版)

P8868 [NOIP2022] 比赛

P8227 「Wdoi-5」建立与摧毁的结界

posted @ 2024-01-06 23:51  Mirasycle  阅读(23)  评论(0编辑  收藏  举报