纯粹分治

概述

  • 纯粹分治是分治思想最纯粹,最朴实,最本质?的应用。

  • 我不太会描述...一个典型是平面分治计数。还是看例题吧...

例题

Public NOIP Round #3 数圈圈

  • 题意:求一个二维矩阵中的圈的数量。所谓圈,就是字母全部相同的一个矩形壳。

  • 数据范围:\(n,m\leqslant 2\times 10^3,6s\)

  • 首先这道题暴力乱搞好像能拿不少分...就是暴力枚举左上角,使用一定的剪枝来枚举长宽。

  • 不过我们来谈谈正解吧:二维分治。

  • 每次取矩阵较长的一边的中点将其分割开,计算跨分割线的圈数。为什么要取较长的在复杂度分析部分会用到。

  • 考虑怎么计算 匚 这种形状的数量,因为两边是对称的,左右分割和上下分割是同构的,所以我们只讨论 匚 的求解:

    • 暴力枚举其在分割线上的上界和下界,不妨记其横坐标分别为 \(u,v(u<v)\),纵坐标为 \(mid\),这里我们采用 OI 坐标系。

    • 整一点悬线法的手法,记 \(U_{x,y},D_{x,y},L_{x,y},R_{x,y}\) 分别为 \((x,y)\) 向上/下/左/右的极大同字母延伸位置(下面使用时可能省略部分下标),于是求的其实是 \(\sum\limits_{i=\max(L_u.y,L_v.y)}^{mid} [D_{u,i}\geqslant v]\)

    • 先考虑 \(L_u.y>L_v.y\) 的情况,此时上式相当于 \(\sum\limits_{i=L_u.y}^{mid} [D_{u,i}\geqslant v]\),显然我们可以通过在 \(u\) 上开 \(D_{u,i}\) 的桶+预处理后缀和来 \(O(1)\) 地算出上式的值。

    • 如果不然,则 \(D\) 变成 \(U\),其实是对称的。

    • 当然后缀和也可以通过树状数组实现,如果把 \((u,i)\) 的影响视为给 \(D_{u,i}\)\(+1\) 的话。因为 \(6s\) 而且 \(n\) 只有 \(2\times 10^3\),纸面也才 \(4.8\times 10^8\) 的双 \(\log\) 做法也是随便过的。

    • 当然,关键是要证明分治本身是 \(O(n^2\log)\) 的。

      • 记当前处理的子矩阵短边边长为 \(x\),长边边长为 \(y\),则预处理部分的复杂度是 \(O(xy)\)

      • 我们的分割线一定是短边,于是枚举是 \(O(x^2<xy)\) 的。

      • 两者合计 \(O(xy)\),那么可以看出,每层是 \(O(nm)\) 的。于是分治的复杂度是 \(O(n^2\log)\)

  • 那么其实一共是八种对称的情况,分讨一下就可以了。实现细节较多,但对称性使得相对可调一些。

CF364E Empty Rectangles

  • 题意:求一个二维 \(01\) 矩阵中的和为 \(K\) 的矩形数量。

  • 数据范围:\(n,m\leqslant 2.5\times 10^3,K\leqslant 6,12s\)

  • 考虑使用二维分治,仍然每次取长边中点划分。考虑怎么求这个东西,我们还是希望枚举其在分割线上的上下端点,求出右边和为 \(x\) 的方案数。

  • 注意到这道题好像没有上一道那种优美的“无关性”,\(u,v\) 之间的那些行的影响不容易快速计算...故转而考虑递推,即在已经知道 \(u,v\) 的方案数的情况下,递推出 \(u,v+1\) 的方案数。

  • 考虑手模一个样例看看:

\[\begin{matrix} 1 & 0 & 1 \\ 0 & 1 & 0 \\ 1 & 1 & 1 \end{matrix} \]

  • 其中最左边一列即为第 \(mid\) 列。

  • 首先考虑 \(u=v=1\) 的情况,此时我们直接暴力扫到左边就行。

  • 然后考虑加入第二行的影响。思考实际意义,发现原本向左延伸 \(1,2,3\) 的矩阵和为 \(1,1,2\),然后现在变成了 \(1,2,3\)

  • 加入第三行,变成 \(2,4,6\)

  • 唔...发现如果把同一列的 \(1\) 垒起来成为一个数,那么它就是和数组的差分数组。看起来有点废话,但这告诉我们,新的 \(1\) 的效果是后缀加...显然可以通过维护差分数组的方式来用树状数组快速求 \(x\) 延伸的矩阵和。

  • 但,怎么求矩阵和为 \(x\) 的延伸有多少种呢?

  • 仍然考虑这个后缀加。它其实是把从它向右的所有延伸带来的矩阵和都 \(+1\)\(1\to 2,2\to 3,etc.\)。设加入其之前,其对应位置的和为 \(x\),则 \(\forall x'>x\),其改变容易计算;关键是和恰为 \(x\) 的那些延伸有多少变了,有多少没变。

  • 考虑暴力!既然 \(K\leqslant 6\),那么这个“分段函数”的拐点至多有 \(6\) 个。称比左边大的点为拐点,强行记录每个拐点的位置,设插入前为 \(x\),则相当于在插入处创建一个 \(x+1\) 的拐点,插入处以右的每个拐点对应的值 \(+1\)\(>6\) 的拐点不必维护,没有价值。

  • 那我们来分析一下这个复杂度。发现...发现崩了啊!从 \(u=v=up\) 扫到 \(u=up,v=dw\)\(O(xy)\) 了,这...这...

  • 考虑进一步挖掘性质,优化计算。注意到 \(K\leqslant 6\),扩展行的 \(0\) 对计数无影响,于是事实上我们只需要维护每一行的至多前 \(7\)\(1\) 的位置。预处理扫一遍的复杂度 \(O(xy)\),从 \(u,v\) 递推到 \(u,v+1\) 的复杂度是 \(O(6\log)\) 左右,相当于 \(O(x^2\log)\)

  • 故总复杂度至少可以分析到 \(O(n^2\log^2)\),纸面 \(9\times 10^8\),应该足够过了。

  • 上面是我想出来的办法...但显然实现太烦人了。看了题解之后,发现有更妙的办法:

  • 我们刚才的所谓拐点,也即 \(sum=x\) 的起始位。我们现在考虑转而维护 \(sum=x\) 的结束位,即定义 \(ri_x\) 表示左边界为 \(u\to v\),内部和 \(\leqslant x\) 的矩形的右边界最大到哪里。

  • 那么显然差分容易求得特定和的方案数,考虑怎么维护这个 \(ri\),仍然使用递推,每次递推之后仍然按顺序考虑前 \(6\)\(1\)

    • 对于 \(ri_x<pos\) 的:没有影响。

    • \(ri_x\geqslant pos\) 的第一个:\(ri_x=pos-1\)。注意有时候可能 \(ri_x=ri_{x-1}\),这也许需要一点特判。

    • 其他:\(ri_x=ri_{x-1}\)。注意转移顺序。

  • 啊...是的。要好得多,还没有树状数组的 \(\log\)。这样一来就能把 \(K\) 的常数考虑进来了,不怕被卡常。

UVA1608 不无聊的序列 Non-boring sequences

  • 题意:判断 \(S\) 是否 boring。所谓 boring,就是存在 \(S\) 的子串 \(s\) 满足 \(s\) 中不存在只出现一次的元素。

  • 数据范围:\(n\leqslant 2\times 10^5\)

  • 注意到若 \(S\) 不 boring,则 \(s_{1\sim n}\) 中必有只出现一次的元素。找到它然后左右区间变成子问题,递归求解即可。

  • 注意到若我们每次都从左向右找,则可能构造一个数列 \(112233445678\) 使得我们的复杂度为 \(O(n^2)\)。改为从两边向中间扫,\(T(n)=T(n-x)+T(x)+O(x)(x\leqslant n-x)\),这个东西的上界是 \(O(n\log n)\)

AT_joisc2017_j 誘拐 2 (Abduction 2)

  • 题意略。

  • 注意到所在道路的权是单调递增的。这就使得某些暴力或许可行...

  • 很容易想到 dp,但显然 T 飞,因为点太多了。注意到直行点是无价值的,且只询问 \(100\) 次,称得上很少了,考虑记忆化,并使用倍增之类的办法来找后继节点,卡卡常能过。但这个做法的复杂度很没道理。

  • 考虑分治。不断对当前矩阵分治。找到最大权边,若在其上则直接选走左/右来计算,否则根据其在哪边递归对应矩阵,重复此过程。显然单个询问的递归总量为 \(O(n+m)\),复杂度有保证。需要维护四至点的值(预先把在边上拐弯的权也算进去)。upd:疑似假解...

posted @ 2023-01-15 10:08  未欣  阅读(17)  评论(0编辑  收藏  举报