根号算法

根号分治

D. Time to Raid Cowavans

这道题相当于天然地对 \(t\) 取模,对于余数也相当于天然分组 于是可以发挥根号的作用了,当模数小于 \(\sqrt n\) 时,通过 \(O(n)\) 预处理和回答 而大于的数,因为暴力是 \(\sqrt n\),直接暴力做即可 对于小于部分的预处理: 可以按模数排序,对于模数相同的一些询问一起搞,每次对每个余数开个 \(vector\),因为起始位置不同,相当于是一段后缀,这个可以通过前缀和处理

然而遇到类似的 原题 后还是不会做,这里是 题解

题意:有根树子树中距离模 \(x\) 等于 \(y\) 的加 \(x\),单点查询


1597 有限背包计数问题

很明显,当体积大于 \(\sqrt n\) 后,数量不断变小,满足了根号分治的性质,考虑以体积为 \(\sqrt n\) 进行分治 对于小于的情况,观察转移式 \(f[j]+=f[j-i*k]\),符合要求的值 \(mod\) \(i\) 是定值,可以维护前缀和优化到 \(O(\sqrt n)\) 对于大于的情况,这里放一张图便于理解

数学中item 数学中的√怎么算_分治

设 \(g[i][j]\) 表示选了 \(i\) 个总大小为 \(j\) 的方案数 有两种转移方式,要么将原来的每个物品体积都增加1,如图红色的情况 要么新增一个更大体积的物品,如图绿色的情况

最后将两个数组乘法原理乘起来即可

然而遇到 原题 以后还是不会做,这里有 题解


Minimize Digit Sum

绝对的神仙题:

对于简单版本,考虑分治进制数 \(B\),当 \(B\) 大于根号时,位数最多两位 即表示成 \(aB+b=n\),答案为 \((a+b)\),那么枚举 \(a\) 即可算出 \(B\) 和 \(b\) 当进制数小于根号时直接枚举进制暴力算


CF1039D You Are Given a Tree

似乎类似于整除分块? 对于较大的 \(k\)


P3645 [APIO2015]雅加达的摩天楼

注意到跨度与个数形成一对互补关系对,那么将 \((位置,跨度)\) 作为最短路状态一定意味着状态数是根号级别的,直接 \(bfs\)


树上GCD

大体思路肯定是容斥,首先计算 \(gcd\) 是 \(i\) 的倍数的情 考虑根号分治,设阈值为 \(k\) 那么对于 \([1,k]\),可以直接保存下来子树内的同余系点的个数,然后转移 如果想要做到空间 \(O(n)\),可以设 \(f_u\) 表示 \(u\) 子树内与 \(dep_u\) 模 \(i\) 同余的个数,设 \(g_u\) 表示 \(u\) 子树内与 \(dep_fa\) 模 \(i\) 同余的个数,然后每个点贡献到 \(i\) 级祖先上 对于 \([k+1,n]\),考虑长剖,那么两个数组的合并为 \(O(n)\) 考虑计算贡献复杂度,由于需要保证较小的那一个深度 \(>k\),这样的双链个数不超过 \(k\),于是复杂度仍然正确

另一种较为暴力的方法是进行有根树的点分治,计算分治中心子树内外的贡献 若 \(lca\) 为 \(u\),可以 \(log\) 计算出 否则设 \(lca\) 为 \(v\),那么要求出 \(v\) 的旁侧链内是 \(d\) 倍数的个数,这个也是 \(log\) 的,但是在 \(u\) 子树中统计的则将是从 \(pos\) 开始的长为 \(d\) 的等差数列的位置上的数,这是经典的根号分治问题,可以做到 \(O(n\sqrt n)\) 通过玄学复杂度分析得出点分治的 \(log\)


根号重构

可以发现在许多高端题目中根号重构加难度的地方在块内的处理方式上,以及块间会有影响


C. 斐波那契

题意:区间加斐波那契,单点查询

考虑根号重构,可以发现重构的过程可以通过差分前缀和 \(O(n)\)


P5443 [APIO2019]桥梁

根号重构的灵活化体现,本质上更加显现出其对时间分块的特性 对于一个时间块,可以把无修改边权、询问边权排序,那么每次动态加入的边就是根号的,用可撤销并查集来维护


小 G 的 DAG

题意:\(DAG\) 上三种操作:给一个点能到达的点和 \(x\) 取 \(min\);给一个点能到达的点覆盖为 \(x\);单点查询

覆盖操作是好做的,考虑先做完覆盖操作,把取 \(min\) 操作插进去 考虑只有在最后一次覆盖操作后的取 \(min\) 操作才是有效的,那么问题变为时间区间查询单点上的 \(min\) 标记,这个可以把基于时间的重构块显式地建出来即可实现区间查询 同时可以发现 \(bitset\)


分块

对于不会做的分块题名之曰:大分块


P4168 [Violet]蒲公英

还是记一下吧,上次考出来给懵住了 事实上就是要维护出来 \([l,r]\) 每个数出现的次数 考虑设整块范围为 \([lx,rx]\),可以知道如果众数没在零散快中出现过那么一定是整块的众数了 如果在零散快中出现过,那么就要统计其出现了多少次,可以预处理出每个前缀整块中每个值出现了多少次就行了


P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I

设中间块为 \(2\),两边散块分别为 \(1,3\) 首先先来列举一下需要处理的的部分:

  1. 1->1
  2. 2->2
  3. 3->3
  4. 1->2
  5. 2->3
  6. 1->3

依次来解决: 看起来 \(1,3\) 比较好做,需要处理出每个块中每个位置到块端点这部分的逆序对数,设为 \(pre\) 和 \(suf\),用树状数组维护 接下来是 \(6\),这个可以把前后两个散块归并排序实现 然后看 \(4,5\),这是考虑维护一个这样的函数:\(f[i][j]\) 表示前 \(j\) 个位置与前 \(i\) 个块的逆序对数 怎样实现呢?对与每个块和全局的数来一次归并即可 有了这个函数,可以紧接着再处理出 \(g[i][j]\) 表示 \([i,j]\) 的块的贡献值,\(2\)

别忘了还得考虑在同一块中的答案 此时只要用 \(pre[r]-pre[l-1]-\)


值域分块

对于一些操作,可以发现使用值域分块时可以做到 \(O(1)\) ~ \(O(\sqrt n)\),适用于修改查询不均衡的情况 一般配合其他根号算法时取得总体平衡复杂度 用途有 \(mex\)

注意在单点修改的背景下值域分块也是可以正常运作的 只不过在零散快重构的时候不可以直接将值域块重构,而是要改成单点修改式重构,用根号的复杂度完成


树分块

在树上撒一些点,是的每个点的根号祖先内一定有关键点 那么维护关键点之间、点到关键点的信息即可实现根号级别的查询了


平面分块

考虑把二维平面分成大小为 \(B\times B\) 的块,那么每次矩形修改查询均需要 \(\frac{n}{B}\times B^2+(\frac{n}{B})^2\),于是 \(B\) 取 \(n^{\frac{1}{3}}\),总复杂度 \(n^{\frac{5}{3}}\)


莫队

莫队用于解决信息不具有可合并性,但是新插入或删除一个值贡献很方便计算的情况 如果只方便插入或删除的一种,可以用回滚莫队来解决 模板


P3674 小清新人渣的本愿

乘法可以暴力枚举,考虑维护加减法 以加法为例,由于值域很小,那么可以开一个 \(bitset\) 存某一个值当前是否出现过,再开一个存 \(N-x\) 是否出现过,然后用 \(bitset\)


P7708 「Wdsr-2.7」八云蓝自动机 Ⅰ

难点在于在序列开头插入一个操作该怎么处理 总之就是维护出来该有的信息那么题目中那几种操作还是可以往前插的(其实是现在不会了)


二次离线

P4887 【模板】莫队二次离线(第十四分块(前体))

考虑暴力莫队的过程,指针每次移动都是 \(O(k)\) 计算的,不能满足要求 那么考虑将移动的区间离线成询问,再次处理 对于区间 \([l,r]\) 移动到 \([l,r+k]\) 那么要对每个 \(x\in [r+1,r+k]\) 求出 \(f(x,[l,x-1])\)\(f(x,[l,x-1])=f(x,[1,x-1])-f(x,[1,l-1])\) 前半部分预处理出来,后半部分离线处理


P5501 [LnOI2019]来者不拒,去者不追

一个数在区间中贡献是 \(rk\times x+\sum y[y\ge x]\) 那么离线后即可用分块 \(O(\sqrt n)\) ~ \(O(1)\)


带修莫队

如果在莫队中加入了修改操作,那么相当于加入了时间这一位 把 \(siz\) 设为 \(n^{2/3}\) 次可以得到 \(n^{5/3}\) 的优秀复杂度 每次移动指针时只要再加上时间维度的移动即可 一定要注意,一个修改可以生效当且仅当在当前左右指针范围内,但是当指针扫过去的时候这个修改已经发生了,只不过是是否对答案产生了贡献,因此这两者一定要注意区分P1903 [国家集训队] 数颜色 / 维护队列


树上莫队

一个伪树上莫队的方法是写出伪欧拉序(即从儿子回来的时候不加入,只在离开的时候加入),然后再这个上面跑序列莫队 对于询问,分类讨论为 \(x\) 是 \(y\) 的祖先,此时询问 \(st_x\) 到 \(st_y\) 的区间 否则询问 \(ed_x\) 到 \(st_y\) 的区间,可以发现此时漏掉了 \(lca\),需要手动加回来模板