根号分治

概述

  • 根号分治,是一种对数据进行分治的分治方式。

  • 具体来说,如果所要求进行的过程满足满足大点、小点(一般以根号为分界,因为这样复杂度最平衡)可以使用不同的方式处理,则可以考虑使用根号分治。一般常见的有两种情况:

    • 根号以下的数据的种类很少,可以全部维护之;根号以上的数据,直接暴力的复杂度可接受。典型代表是质因数分解,此时也许可以叫数论分治。

    • 和上面刚好反过来。典型代表是对图按度数分治。当然事实上说两者一样也是对的,毕竟根号分治本质上就是把两种暴力拼到一起。

  • 一定程度上,根号分治也是一种数据分治,只不过不是数据点分治罢了(笑)。

P3396 哈希冲突

  • 题意:给定序列 a1n,有 m 个操作,为单点修改或求 imodp=xai

  • 数据范围:n,m1.5×105,p<n

  • 注意到传统方法,譬如线段树和分块,都不能很好地处理 modp=x 的和,究其原因是 p 太多了,不同的 p 之间又互相难以复用。

  • 但观察到对于大数即 np,直接暴力的复杂度是 O(nn)

  • 于是考虑根号分治,小数只有 O(n) 个,修改时直接暴力修改。

  • 总复杂度 O(n)

P3939 数颜色

  • 题意略。大抵我就是数据结构学傻了吧...

  • 考虑根号分治,小的用 set,大的用 ta(一开始还想着分块结果发现这里 ta 更优)。O(nnlog)

  • 发现小的部分复杂度很高,不太美妙。考虑换链表,去一个 log,修改暴力修就可以。O(nn)

  • 注意到这是排列链表,不妨乱搞一下。维护出现位置行不行?好像可以,然后直接二分...那我要链表干啥?O(nlog)

  • 主席树。看起来不卡空间。O(nlog)

  • 差分,然后 cdq。O(nlog)

  • 显然我选直接二分。。。

P1989 无向图三元环计数

  • 题意略。这是非常经典的一道根号分治了...但我要先谈一个邪道做法。

  • bitset!暴力没有 bitset 怎么行!容易想到一个 bitset 维护连通性,然后枚举每条边把两个端点的出边与起来 count 一下的做法,T=O(nmw),然而发现空间复杂度为 O(n2),开不下。

  • 怎么办?暴力呗!根号分治,大度点开 bitset,小度点直接暴力枚举出边处理之,T=O(nmw+mm),M=O(nm)。bitset 的常数比较小,所以是可以随便乱搞过去的。

  • 当然啦,我们不提倡这个...毕竟三元环计数的正解非常妙,学一下。

  • 核心思路:将环按照我们希望的方式定向。

    • 注意到上面的做法中每个环会被统计三次,则如果我们能把环变成有向的(当然必须是等价变换),在这一过程中人为限制大度点的出度,则问题解决。

    • 具体地,规定每条边由度小的点指向度大的点。若同度,则由编号小的点指向编号大的点。

    • 发现这样的生成图一定是无环的,毕竟都严格全序了,原图上所有环现在都是一个角+一条边。考虑枚举角的第一个边 uv,然后对每个 vw 检查是否有 uw

    • 检查通过预标记实现。这一做法的复杂度一定是正确的,因为其复杂度可以变换为 eoutet,而 outO(m),证明如下:

    • 对于小度点,显然生成图上度不增。

    • 对于大度点,度数不小于它的点只有 O(m) 个。

  • 综上,复杂度为 O(mm),比 bitset 更优且更优雅。

CF444D DZY Loves Strings

  • 题意略。

  • 乍一看没什么思路,不妨从暴力想起。一个显然的暴力就是枚举出现次数少的串的出现,在出现次数大的串的出现序列上二分来计算答案。

  • 这一做法的问题主要有两个:第一,不知道出现;第二,会 T。

  • 显然 KMP 这种 O(|P|+|T|) 的求出现算法没机会,考虑 AC 自动机。我们知道 AC 自动机可以求每个模式串在文本串中的出现次数,但位置不太好办。考察其过程,发现本质上是 dfs 完之后做树形 DP,之所以位置不好办是因为要处理位置复杂度就变成了 ipathdepi,其中 path 为 dfs 时经过的路径的可重集合,但这里 dep4,故可以直接做。

  • 事实上并不需要这么麻烦。上面的 dep4 的实质不就是串长嘛,则直接枚举左端点 substr 创过去就可以了。

  • 设法构造一下 ||n(记 n=|S|)的串最多有多少个吧,将 S 分为 n 块,让串在每块中都出现一次,则一块中至多有 O(n) 个不同子串是输入串。性质不错,考虑根号分治。

  • 对于出现次数 n 的串,暴力枚举其出现,对每个相关询问在对应串的出现上二分算答案,每个询问至多被处理 n 次,复杂度为 O(qnlog)

  • 此时只剩下两个串出现次数都 >n 的询问,考虑预处理,即关于第一个串在 S 上扫一遍,暴力维护包含 Tii+x 和第一个串在 i 左/右出现的子串的最小长度,O(nn)

  • 从而可以放弃上面的二分,把大部分事情交给预处理,对都是小次数串的直接双指针。O(qn)

CF1039D You Are Given a Tree

  • 题意略。差点被题解骗了以为可以整体二分...然而直接整的话是 O(n2log2) 的...

  • 本题其实比较数论分治。很容易注意到类似数论分块的性质,考虑对于固定的 k 怎么求解,发现其是一个赛道修建的退化版(不是边独立而是点独立),不用配对所以单轮 O(n)

  • 那么解很显然了,阈值以下的暴力求,阈值以上的二分答案检验可行的最大边数。显然对每个都二分是不可接受的,故可以考虑使用整体二分实现(只有 nB 条不同的链,链长为 O(lognB))但不优美,不妨利用单调性,首先对最小者计算答案,得到答案后二分边数一次决定下一个点在哪里,于是复杂度为 O(nBlog),稍微平衡一下,得到 B=nlog,总复杂度为 O(nnlog)

P5901 [IOI2009] regions

  • 题意略。审完之后就觉得是一个很麻烦的根号分治...

  • 考虑在线线段树合并,求得 sumi,c 表示 i 的子树中颜色 c 的有多少种。这个 log 真的好烦啊...然而空间根本开不下...这部分复杂度 O(nlogn)(直接认为颜色数和 n 同阶算了)。

  • 将色分为小色和大色。对于小色,如果 c1 是它,直接上去询问,O(nBlog);对于大色,转而考虑反演,对每个点维护它的祖先中每个大色有多少个点,过程中对对应大色贡献答案,O(nnB)

  • 显然这个 log 我们不喜欢,log 倒是无所谓但是线合很麻烦啊,数据结构是脑子的平替,不能对标脑子。我们考虑将询问离线,把大色相关询问按上面那样处理,小色相关询问按 c2 排序,对每个 c2 跑一遍,直接整显然复杂度还是不对是 O(n2),但我们可以转而考虑树剖,区间修改到根即可,复杂度为 O(nlog2+nBlog),至少比在线线合好不少。

  • 平衡一下复杂度,得到 O(nnlog)。行吧行吧~

  • p.s.实践表明大色部分不知道为什么跑得特别慢,这里平衡复杂度不如不平衡。奇怪...

CF103D Time to Raid Cowavans

  • 题意略。不管怎么看都是很典的根号分治吧?

  • 首先对于 kB,直接暴力之。对于 k<B,预处理即可,乍一看有 O(n)(k,t) 对还要枚举 t+xk 不太对,然而实际上对每个 k,复杂度为 O(k×nk),故复杂度为 O(nB)。啊?kt?差分啊。

  • 总复杂度为 O(n(B+nB)),也没啥操作空间,直接 n 了事。O(n) 的空间是不可能被卡的。为什么 O(n)?允许离线的啊。对每个小 k 单独做一遍不就好了。

P5309 [Ynoi2011] 初始化

  • 题意略。

  • 考虑直接模仿上一道题,根号分治,大的暴力,小的加到对应的剩余系上。注意到询问不太好搞。

  • 暴力修的内容线段树维护一下就好了吧。也可以树状数组。考虑剩余系上的东西,枚举模数 x,应该是把整个剩余系从 lmodx 开始遍历了不知道多少遍然后剩一段,故我们可以对每个剩余系开一个 ta 来维护之。

  • 故复杂度分别为 O(nB)O(log)O(log+Blog),操作一下可以把 log 平衡进去。然而似乎还是可能会被卡...

  • 看了一眼题解,果然被卡了。考虑更高效的算法,把 log 去掉。众所周知整个 生态和 log 生态就不搭,把 ta 改成序列分块试试。

  • 复杂度变成 O(nB)O(B)O(B+nB)。果然,这样好了不少,就是处理系的贡献时细节比较多。事实上我们可以放弃在系上开 ta,直接在系上维护前后缀和即可,复杂度不变,毕竟系的长度这么短。

  • p.s.因为出题人试图卡各种假做法,小系特别多。考虑调块长,毕竟我们的小系常数特别大,我取 n0.395 过的。

P5355 [Ynoi2017] 由乃的玉米田

  • 题意略。

  • 前三种操作见莫队-莫队配合 bitset-小清新人渣的本愿。我们只谈第四种。

  • 根号分治。对 >n 的,暴力之,反正我们手里有每个数字是否出现的 bitset。

  • n 的,借用一下线段树求出现那边的手法,即对每个 xn,维护一个 prei 表示满足 aiaj=xajai=x 的,i 的最大 j。显然这容易单次 O(n) 做到。

  • 乍一看好像得整个 ST 表,会带 log...但事实上因为 preii,我们可以强制 prei=max(prei,prei1),然后判一下 prerl 就好了。O(n(n+nω))

  • 注意有点卡空间,O(nn) 个 int 有点开不下,还是把对阈值以下的询问离线下来挨个整吧。

posted @   未欣  阅读(1006)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示