2024.12.18 根号科技 讲课笔记
以下除了特殊说明的,块长默认取 \(\sqrt n\)
序列分块
静态序列区间查询排名
给定 \(a_{1\sim n}\),\(q\) 次查询 \([l,r]\) 内 \(\le v\) 的数量(假定 \(n,q\) 同阶)
思路 1
二维数点。若可以离线,则扫描线解决,时间复杂度 \(O(n\log n)\),空间 \(O(n)\)。若强制在线,则可以树套树解决,时间和空间都是 \(O(n\log^2 n)\);或 \(KDT\) 解决,时间 \(O(n\sqrt n)\),空间 \(O(n)\)
思路 2
分块。假设块长为 \(B\)。预处理每块内排好序的结果。若在同一块或边角块则暴力,时间复杂度 \(O(B)\)。整块则在排序的结果中二分,时间复杂度 \(O(\frac nB\log B)\)。总时间复杂度 \(O(nB+\frac{n^2}B\log B)\)。当 \(B\) 取接近 \(O(\sqrt {n\log n})\) 时最优,实际可以略小一点,因为前面 \(O(B)\) 部分常数更大
动态序列区间查询排名
在静态的基础上增加单点修改或区间加操作
思路 1
带修二维数点。扫描线无法使用。若只有单点修改,则 \(KDT\) 和树套树都能使用,时间复杂度不变。若区间加,则三种方法都无法使用
思路 2
若单点修改,就把修改的值和那块中剩余值归并。若去就按加,则整块打标记,边角或同一块中则把加的部分和没有加的部分归并。两者单次都是 \(O(B)\)
在线区间众数:P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III
查询众数出现次数,\(n,q,a_i\le5\times 10^5\),\(2s\),\(0.5G\)
将每个数的出现位置保存在 vector 数组中,空间复杂度 \(O(n+V)\),时间复杂度 \(O(n)\)
一种较劣的方法为枚举边角块中出现的所有数和整块的众数,分别查询区间中出现次数(容易在 vector 上二分单次 \(O(\log n)\) 实现),时间复杂度 \(O(n\sqrt{q\log n})\),显然过不了
考虑枚举答案
先预处理出 \(f_{i,j}\),表示第 \(i\) 块到第 \(j\) 块的众数出现次数,容易时间复杂度 \(O(\frac {n^2}B)\),空间复杂度 \(O(V+\frac{n^2}{B^2})\) 处理
查询时,对于散块暴力即可,单次 \(O(B)\)
否则先令 \(ans\) 等于中间整块的众数出现次数
枚举左侧散块中每个数,查询其之后第 \(ans\) 个与它相同的数是否存在且在区间中(容易通过预处理每个位置在 vector 中下标 \(O(1)\) 实现),若是,则令 \(ans\) 加一并继续查询,否则检查下一个。右侧类似
总时间复杂度 \(O(\frac{n^2}{B^2}+qB)\),令 \(B=\frac n{\sqrt q}\) 即可做到 \(O(n\sqrt q)\),空间复杂度 \(O(n+V+q)\)
可能需要一定程度的卡常
在线区间逆序对:P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I
\(a\) 为排列,\(n,q\le10^5\),\(750ms\),\(0.5G\)
预处理每块排序后的结果(含原下标)\(N\),每块内逆序对 \(cnt_i\),每个位置到所在块左边界范围内的逆序对 \(Ls_i\),到右边界的逆序对 \(Rs_i\)。这部分时间复杂度为 \(O(n\log n)\),空间复杂度 \(O(n)\),不会成为瓶颈
令 \(Ct_{i,j}\) 为 \(1\sim i\) 块中 \(\ge j\) 的数量,时空复杂度都是 \(O(\frac {n^2}B)\)
令 \(bt_i\) 为 \(i\) 所在块编号,\(L_i,R_i\) 为块 \(i\) 的左右边界,则预处理出 \(\large Ls_{i,j}=\sum_{x=L_i}^{R_{bt_j-1}}\sum_{y=L_{bt_j}}^{j}[a_x>a_y]\),\(\large Rs_{i,j}=\sum_{x=j}^{R_{bt_j}}\sum_{y=L_{bt_j+1}}^{R_i}[a_x>a_y]\),可由 \(Ct\) 求出,时空复杂度都是 \(O(\frac{n^2}B)\)
由 \(cnt\) 和 \(Ls\) 可求出 \(F_{i,j}\),表示 \([L_i,R_j]\) 范围内的逆序对数,时间复杂度 \(O(\frac{n^2}{B^2})\)
这样查询时,整块到整块,边角块内部,边角块到整块,这三部分的贡献都预处理了,还剩下边角块到边角块,两端点在同一块的情况要处理
前者等价于求 \(Tmp(L_1,R_1,L_2,R_2)=\sum_{x=L_1}^{R_1}\sum_{y=L_2}^{R_2}[a_x>a_y]\),满足 \(L_1\) 和 \(R_1\) 在同一块,\(L_2\) 和 \(R_2\) 在同一块,\(R_1<L_2\)
后者等于 \(Tmp(L_{bt_l},l-1,r+1,R_{bt_r})+Ls_r+Rs_l-cnt_{bt_l}\)
因此问题转化为求 \(Tmp\)
先将 \([L_1,R_1],[L_2,R_2]\) 中的数分别有序地取出,容易通过数组 \(N\) 在 \(O(B)\) 内实现
然后按照类似归并排序的方式合并两者,求出两者之间逆序对数量,这部分时间复杂度也是 \(O(B)\)
因此总时空复杂度 \(O(\frac{n^2}B+qB)\),当 \(B=\frac n{\sqrt q}\) 时时空复杂度都为 \(O(n\sqrt q)\)
可能需要适当增大块长(代码中用了 \(B=1.7\sqrt n\))以卡常
P4117 [Ynoi2018] 五彩斑斓的世界
每次将区间中 \(>x\) 的减去 \(x\),或查询区间中某数出现次数,\(n\le10^6,q\le5\times10^5,0\le a_i,x\le10^5+1\),\(7.5s\),\(64M\)
将序列分块,对于非整块的修改暴力重构块并跟新,非整块的查询暴力枚举
考虑用并查集维护值相同的下标
对于整块的修改,令最大值为 \(mx\),考虑势能分析
若 \(x\ge mx\) 直接忽略
若 \(2x\le mx\),则令 \(1\sim x\) 代表的集合依次合并至 \(x+1\sim 2x\) 代表的集合,并打上区间减 \(x\) 的标记,用 \(O(x)\) 的复杂度(据说此题并查集修改查询方式特殊,不用乘以 \(\alpha(n)\))令 \(mx\) 减小了 \(x\)
否则令 \([x+1,mx]\) 代表的集合依次合并至 \([1,mx-x]\) 代表的集合,\(O(mx-x)\) 内令 \(mx\) 至少减少 \(mx-x\)
但这样每块都要开 \(O(n)\) 的空间,总计 \(O(n\sqrt n)\) 的空间,无法承受
因此离线所有操作,枚举每一块,依次处理即可(查询的结果可以累加)
时间复杂度 \(O(n\sqrt n)\)(假设 \(n,q\) 同级),空间复杂度 \(O(n)\)
根号复杂度优化
在一些情况下将修改和查询中一个降到 \(O(1)\),从而消除瓶颈,优化复杂度
\(O(1)\) 单点修改,\(O(\sqrt n)\) 区间查
仅维护区间和,查询时将边角和中间若干块暴力拼接
要求维护信息有结合律,且修改可以直接作用到区间和上,或信息可差分(从区间和中减去再加上)
可以用其实现 \(O(n\sqrt n+m)\) 的 \(dijkstra\)
\(O(1)\) 区间修改,\(O(\sqrt n)\) 区间查
类似树状数组区间修改,区间查询的方式,将其拆为两个单点修改,区间查询
要求信息可差分,有交换律,结合律,且可以 \(O(1)\) 算出差分信息的若干倍
\(O(\sqrt n)\) 单点加,\(O(1)\) 区间和
对于每块,维护第一块到当前块内所有信息之和。每个位置维护其所在块左端点到其的信息和。修改时暴力重计算其所在块内和全局的前缀和。查询时拆为两个前缀和的差,然后将整块和剩余的拼起来
要求信息有结合律,可差分
\(O(\sqrt n)\) 区间修改,\(O(1)\) 区间和
维护信息同上,修改时暴力重构边角块,中间的打标记
要求信息有结合律,可差分,且标记有结合律,可根据区间和的信息计算出作用标记后和的信息
\(O(\sqrt n)\) 单点修改,\(O(1)\) 区间最值
区间最值不可差分,因此不能用 \(O(\sqrt n)\) 单点加,\(O(1)\) 区间和的方式计算
注意 \(ST\) 表单点修改时间复杂度为 \(O(n)\) 的
因此每块保存一个 \(ST\) 表,块之间维护一个 \(ST\) 表即可
若将 \(ST\) 表换为猫树,则可将区间最值换成可以与维护信息构成半群的运算
静态区间 RMQ
构建 \(O(n)\),单次查询期望 \(O(1)\),最劣 \(O(\sqrt n)\)
分块,预处理整块之间的信息,块内前缀后缀信息,对于跨块的查询直接拼起来。对于块内的查询,暴力计算,可证其出现的概率为 \(O(\frac 1{\sqrt n})\),单次 \(O(\sqrt n)\),因此总计期望 \(O(1)\)
若将块长设为接近 \(\sqrt n\) 的数,且将第一块和最后一块的长度设为相对较小的,则很难卡(要卡掉至少要 \(O(\sqrt n)\) 个都落到同块内,但出数据的无法确定你具体块长,若区间短则暴力可过,区间长则无法落入同一块)
例题:P3793 由乃救爷爷(此题也可用之后一种实现过)
构建 \(O(n)\),单次查询期望 \(O(1)\),最劣 \(O(\log n)\)
块长换为 \(O(\log n)\),块之间的查询换为长 \(O(\frac n{\log n})\) 的 \(ST\) 表,其余基本相同
构建 \(O(n\log\log n)\),单次查询 \(O(1)\)
\(sqrt\) \(\,tree\) 即可
构建 \(O(n)\),单次查询 \(O(1)\)
在 构建 \(O(n)\),单次查询期望 \(O(1)\),最劣 \(O(\log n)\) 算法的基础上,块长换为 \(\frac{\log n}2\)。跨块的不变,对于零散部分,预处理块内数的笛卡尔树,求出其欧拉序,则转化为 \(0/1 RMQ\),直接状压即可
但是常数较大,可能不如之前的算法
静态区间半群信息
块长 \(O(\log n)\)。块间 \(ST\) 表。块内建猫树,做到 \(O(n\log \log n)\) 预处理,\(O(1)\) 查询;或建线段树,\(O(n)\) 预处理,\(O(\log\log n)\) 查询
据说最优可以到 \(O(n\alpha(n)+q)\)
块状链表
\(O(\sqrt n)\) 插入,删除,随机访问
__gnu_cxx::rope 的底层实现为可持久化块状链表或可持久化平衡树,支持以上操作
莫队
本质为规划一条较优路径(曼哈顿距离),穿过平面上(若三维莫队则为空间中)若干个点
若最优则为旅行商问题,显然不可行
莫队时间复杂度保证,与最优解最劣情况相同,因此足够优秀
一般来说 \(lOr\) 平面上 \(l>r\) 部分未定义,因此需要注意四个指针移动顺序
可以理解为二维扫描线
在线莫队本质为在平面上预处理出 \(B\times B\) 个点的信息(其均匀分布),查询时移到最近的点并加上这部分的信息,要求计算莫队时额外信息不超过 \(O(\sqrt n)\)
莫队卡常时可以使用奇偶优化和函数前加 inline,都能显著提升效率
例 1:AT_tenka1_2014_final_d 高橋君
\(q\) 次询问 \(n,k\),计算 \(\sum_{i=0}^k C_n^i\),\(n,k,q\le10^5\),\(6s,256M\)
发现 \(n\pm 1\) 和 \(k\pm 1\) 都可以 \(O(1)\) 计算,因此类似莫队处理即可
例 2:P4396 [AHOI2013] 作业
\(q\) 次查询 \(l\sim r\) 中在 \([a,b]\) 范围内的数量 和 在 \([a,b]\) 内且出现过的数的数量,\(n,q,a_i\le10^5\),\(1s,512M\)
莫队,前者等于查询桶的区间和,后者等于查询桶区间中有值的数量,使用 \(O(1)\) 修改,\(O(\sqrt n)\) 查询区间和的分块即可,时间复杂度 \(O(n\sqrt q+q\sqrt n)\)
例 3:P5355 [Ynoi2017] 由乃的玉米田
给定 \(a_{1\sim n}\),\(q\) 次查询 \([l,r]\) 中是否存在 和 / 差 / 积 / 商 为 \(x\) 的两个数(可以相同),\(n,q\le10^5,1s,128M\)
离线操作,莫队时维护两个 bitset bs1 和 bs2,bs1[i] 表示当前区间是否存在 \(i\),bs2[i] 表示当前区间是否存在 \(100000-i\)
对于查询和为 \(x\) 的查询,答案为 (bs1<<x&bs1).any()
对于差为 \(x\) 的查询,答案为 (bs1<<(100000-x)&bs2).any()
对于积为 \(x\) 的查询,暴力枚举 \(1\sim\lfloor\sqrt x\rfloor\),直接查询即可
对于商为 \(x\) 的查询,若 \(x>SV\)(其中 \(SV\) 为值域开根),则暴力枚举 \(1\sim\lfloor\frac nx\rfloor\) 查询即可,对于 \(x\le SV\) 的在莫队之外处理
显然上述时间复杂度不超过 \(O(n\sqrt q+\frac{qV}\omega+q\sqrt V)\)(其中 \(V\) 为值域)
枚举 \(x\)(\(1\le x\le SV\)),则还需要在 \(O(n+V)\) 的时间内处理所有形如 区间 \([l,r]\) 中是否存在两数的商为 \(x\) 的查询
从 \(1\) 扫到 \(n\),令 \(ls_v\) 表示到目前为止 \(v\) 最后一次出现位置(没出现则为 \(0\)),令 \(lp_r\) 表示区间右端点为 \(r\) 时,左端点的最大值使得区间中存在商为 \(x\) 的
可得 \(lp_r=\max(lp_{r-1},ls_{a_r/x},ls_{a_rx})\)(后两种需要判是否整除和是否在范围内)
对于询问 \((l,r)\),若 \(lp_r\le l\) 则存在,否则不存在
总时间复杂度 \(O(n\sqrt n+\frac{n^2}\omega)\)(假设 \(n,q,V\) 同级),空间复杂度 \(O(n)\)
例 4:P4688 [Ynoi2016] 掉进兔子洞
给定 \(a_{1\sim n}\),\(q\) 次询问给定 \(l_1,r_1,l_2,r_2,l_3,r_3\),查询将区间 \(a[l_1:r_1]\),\(a[l_2:r_2]\), \(a[l_3,r_3]\) 中都有的数删去,三个区间还有多少数(相同的数重复算),\(n,q\le10^5,a_i\le10^9\),\(3s,500M\)
可以转化为求三个区间内值的并
离散化,然后莫队求出每组询问三个区间对应的 bitset(每个位置表示是否存在这个数)并取 and,则得到的 bitset 的 count 就是答案
由于数字可能重复,bitset 中保存时需要特殊处理,具体见代码
发现这样空间不足
因此将询问分块,每块进行一次莫队,块长取 \(4000\) 以下就能开了
高维莫队
\(k\) 维莫队时间复杂度为 \(O((n(k-1))^{\normalsize 2-\frac 1k})\)
常用于带修莫队
模板:P1903 [国家集训队] 数颜色 / 维护队列 \(\quad\) 代码
回滚莫队
一般为只加入不删除
同块内的直接暴力
按左端点所在块为第一关键字,右端点为第二关键字排序(都是升序)
若当前询问左端点与上一次不在同一块,则暴力清空,并将莫队右端点移到当前询问左端点所在块的右端点,莫队左端点移到莫队右端点减一的位置
暴力扩展右端点并加入
扩展左端点时需要记录修改前的值,求出答案后恢复
时间复杂度 \(O(n\sqrt q)\),与一般莫队相同,但常数大
模板题:
P5906 【模板】回滚莫队&不删除莫队 \(\quad\) 代码
AT_joisc2014_c 歴史の研究 \(\quad\) 代码
bzoj#P4358. permu \(\quad\) 代码
也可以只删除不加入(例如莫队维护链表),类似只加入的逆过程,按左端点所在块升序为第一关键字,右端点降序为第二关键字排序
模板:
QOJ # 1877. Matryoshka Dolls \(\quad\) 代码
莫队二次离线
将莫队这个离线的过程再次离线
模板题:
P4887 【模板】莫队二次离线(第十四分块(前体)) \(\quad\) 代码
P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II(离线区间逆序对) \(\quad\) 代码
P5398 [Ynoi2018] GOSICK(第十四分块) \(\quad\) 代码
一般地,若存在静态序列 \(a_{1\sim n}\),每次询问查询 \(\sum_{l\le i,j\le r}f(i,j)\)
令 \(F(i,j)=f(i,j)+f(j,i)\),\(T(x)=f(x,1\sim x)\)(\(\sim\) 表示将取值范围内的表达式值求和,下同),\(Tp(x)=f(x,x)\)
考虑莫队过程中四种指针移动对答案(莫队维护的答案)的影响:
- \(l\gets l-1\),答案增加 \(f(l-1,l\sim r)+f(l\sim r,l-1)+f(l-1,l-1)\),化简为 \(F(l-1,1\sim r)-T(l-1)+Tp(l-1)\)
- \(r\gets r+1\),答案增加 \(f(r+1,l\sim r)+f(l\sim r,r+1)+f(r+1,r+1)\),化简为 \(-F(r+1,1\sim l-1)+T(r+1)-Tp(r+1)\)
- \(l\gets l+1\),答案增加 \(-f(l,l+1\sim r)-f(l+1\sim r,l)-f(l,l)\),化简为 \(-F(l,1\sim r)+T(l)-Tp(l)\)
- \(r\gets r-1\),答案增加 \(-f(r,l\sim r-1)-f(l\sim r-1,r)-f(r,r)\),化简为 \(+F(r,1\sim l-1)-T(r)+Tp(r)\)
由此可得:
- \(l\gets l-p\) 时,增加 \(F(l-p\sim l-1,1\sim r)-T(l-p\sim l-1)+Tp(l-p\sim l-1)\)
- \(r\gets r+p\) 时,增加 \(-F(r+1\sim r+p,1\sim l-1)+T(r+1\sim r+p)-Tp(r+1\sim r+p)\)
- \(l\gets l+p\) 时,增加 \(- F(l\sim l+p-1,1\sim r)+T(l\sim l+p-1)-Tp(l\sim l+p-1)\)
- \(r\gets r-p\) 时,增加 \(F(r-p+1\sim r,1\sim l-1)-T(r-p+1\sim r)+Tp(r-p+1\sim r)\)
注意要按一般莫队的顺序,不能出现非良定义区间
四个式子都是 \(\pm F(...)\pm T(...)\pm Tp(...)\) 的形式,其中 \(T\) 和 \(Tp\) 的值一般可以 \(O(n\sqrt m)\) 内预处理前缀和,不会成为瓶颈,\(F\) 都为 \(F(l\sim r,1\sim x)\) 的形式,即 \(f(l\sim r,1\sim x)+f(1\sim x,l\sim r)\),可以对 \(1\sim x\) 扫描线,\(l\sim r\) 暴力求和(一般为了保证复杂度,都是 \(O(1)\sim O(\sqrt n)\) 修改,\(O(1)\) 单点查询)
若普通莫队指针移动一次要 \(O(k)\),则一般莫队二次离线(一般扫描线过程中查询 \(O(1)\),修改总时间复杂度不超过 \(O(n\sqrt m)\))可以把复杂度由 \(O(kn\sqrt m)\) 降至 \(O(n\sqrt m+kn)\)
另一种常见的情况是 \(f(l,r)\) 只有 \(l\le r\) 时值不为 \(0\),则用类似的方式,令 \(Tl(x)=f(1\sim x,x)\),\(Tr(x)=f(x,x\sim n)\)(两者分别预处理),可得四种移动对答案的增量为
- \(l\gets l-p\):\(+Tr(l-p\sim l-1) - f(l-p\sim l-1,r+1\sim n)\)
- \(r\gets r+p\):\(+Tl(r+1\sim r+p) - f(1\sim l-1,r+1\sim r+p)\)
- \(l\gets l+p\):\(-Tr(l\sim l+p-1) + f(l\sim l+p-1,r+1\sim n)\)
- \(r\gets r-p\):\(-Tl(r-p+1\sim r) + f(1\sim l-1,r-p+1\sim r)\)
树上莫队
用莫队处理关于子树或链的信息
普通树上莫队
对于子树,可以将原树按 \(dfn\) 映射为序列,原树的子树对应序列的区间,转化为区间查询
对于链,先求出序树的括号序(进入子树记录一次,退出记录一次),得到长 \(2n\) 的序列
令 \(fs_i,ls_i\) 分别为 \(i\) 在括号序中第一次出现和最后一次出现的位置
对于树的一条链 \(u-v\),假定 \(fs_u\le fs_v\)
- 若 \(u\) 为 \(v\) 的祖先(显然 \(v\) 不可能为 \(u\) 的祖先),则 \(u\) 到 \(v\) 的路径上的点的集合为括号序区间 \([fs_u,fs_v]\) 内恰好出现一次的点的集合
- 若 \(u\) 和 \(v\) 没有祖先关系,则 \(u\) 到 \(v\) 的路径上的点的集合为括号序区间 \([ls_u,fs_v]\) 内恰好出现一次的点的集合并上 \(\operatorname{lca}(u,v)\)
在莫队过程中,记录当前区间中每个节点出现次数,若加入的点已经出现了,则删除,其他情况同理
时间复杂度和普通莫队一样,但常数较大
支持带修,但应该不能回滚(因为一部分需要额外加入 \(\operatorname{lca}\) 并删除)
普通树上莫队:SP10707 COT2 - Count on a tree II \(\quad\) 代码
带修树上莫队:P4074 [WC2013] 糖果公园 \(\quad\) 代码
双子树树上莫队
每次查询两个子树中信息的并
本题四维莫队实现优秀可过,因为常数不大
将 \(dsu\;on\;tree\) 的过程写为序列(加入为 \(+x\),删除为 \(-x\)),则每个子树对应序列的一个前缀
由此双子树变为双序列前缀,按一般莫队移动指针即可,时间复杂度 \(O(N\sqrt m)\),其中 \(N\) 为 \(dsu\;on\;tree\) 的序列长度,为 \(O(n\log n)\)
本题需要 \(dsu\) 配合值域分块,做到 \(O(n\log n\sqrt m+m\sqrt V)\)
注意莫队的最优块长为 \(O\left(\frac{n\log n}{\sqrt m}\right)\),不是 \(O\left(\frac n{\sqrt m}\right)\)
也存在 \(O(n\sqrt{m\log n})\) 甚至 \(O(n\sqrt m)\) 的算法
根号分治、根号平衡
例 1:P3396 哈希冲突
长为 \(n\) 的序列,单点修改,或给定 \(x,y\) 查询 \(\bmod x=y\) 的位置值之和,\(n,m\le150000\),\(1s,128M\)
维护 \(sm_{x,y}\),表示 \(\bmod x=y\) 的位置值之和,其中只保存 \(x\le \sqrt n\) 的部分,容易修改时 \(O(\sqrt n)\) 维护
查询时,若 \(x\le \sqrt n\) 则可 \(O(1)\) 查询,否则暴力求和,时间复杂度 \(O(\sqrt n)\)
总时间复杂度 \(O((n+m)\sqrt n)\)
例 2:P3203 [HNOI2010] 弹飞绵羊
给定 \(a_{0\sim n-1}\),令 \(f^1(i)=i+a_i\),令 \(f^k(x)=f(f^{k-1}(x))\),\(m\) 次操作,每次单点修改 \(a_x\),或对于给定的 \(x\),查询最小的 \(t\) 使得 \(f^t(x)\ge n\),\(n\le2\times10^5,m\le10^5\)
对序列分块,块长为 \(B\)
对于每个位置 \(p\),求出最小的 \(t\) 使得 \(f^t(p)\) 不在当前块中,记为 \(c_p\),令 \(p_t=f^{c_p}(p)\)
同块内的 \(s\) 容易 \(O(B)\) 递推算出
查询时,按 \(c\) 向后跳,同时用 \(p\) 计算步数,每次至少向后一个块,时间复杂度为 \(O(\frac nB)\)
修改时暴力重构所在块,时间复杂度 \(O(B)\)
总时间复杂度 \(O(n+m(B+\frac nB))\),取 \(B=\sqrt n\) 则时间复杂度 \(O(n+m\sqrt n)\)
例 3:[ABC219G] Propagation
给定 \(n\) 点 \(m\) 边的无向图,点有点权 \(a_x\),\(q\) 次操作,每次给定 \(u\),令 \(a_{v\mid \exists(u,v)\in E}=a_u\),求出所有操作完成后每个点的点权,\(n,m\le2\times10^5\)
令 \(d(x)\) 为 \(x\) 的度数
每个 \(d(x)>\sqrt{2m}\) 的点记录其最后被操作的时间 \(tg_x\)
所有点记录当前颜色,和其被赋值的时间
操作点 \(x\),若 \(d(x)\le \sqrt {2m}\),则遍历所有度数 \(>\sqrt{2m}\) 的邻居,与 \(x\) 的信息比较求出 \(x\) 的点权,并暴力修改每个邻居的点权;若 \(d(x)>\sqrt {2m}\),则直接修改 \(tg_x\)
时间复杂度 \(O((n+q)\sqrt m)\)
相关结论
- 对于可重集 \(S\),满足 \(\forall u\in S(u\in\mathbb N^+)\),且 \(\sum_{u\in S} u = n\),则 \(S\) 中至多只有 \(O(\sqrt n)\) 种值
- 有数组 \(a_{1\sim n}\;(a_i\in\mathbb N)\),若 \(\sum_{i=1}^n i\cdot a_i=n\),则 \(\sum_{i=1}^n \log (a_i+1)=O(\sqrt n)\)
- \(\sum_{i=1}^n\log(\frac ni)=O(n)\)
更多习题
定期重构、时间分块、操作分块
普通定期重构
有静态数据结构(常为 \(KDT\),\(ACAM\) 等不易快速修改的数据结构),将修改暂存,则每次询问在此数据结构上查询,并考虑暂存的操作对其的影响,若暂存的操作达到 \(B\) 则重构此数据结构
设重构时间复杂度 \(O(R(n))\),在数据结构上单次查询时间复杂度 \(O(Q(n))\),考虑每个暂存操作的时间复杂度为 \(O(K)\),则总时间复杂度为 \(O(\frac qB R(n)+qQ(n)+qBK)\)
取 \(B=\sqrt{\frac {R(n)}k}\),则时间复杂度 \(O(q(\sqrt{R(n)k}+Q(n)))\)
一般来说 \(T(n)\) 常与数据结构的大小有关,此时可以二进制分组:维护 \(2^p\;(0\le p\le \log_2(n))\) 的数据结构各至多一个,每次修改时相当于新加入一个大小为 \(1\) 的,每次若存在两个大小相同的则合并为一个大小为原先两倍的。这样维护时间复杂度只比静态的多一个 \(\log\)
定期重构链表 \(ODT\):ODT的映射思想的推广
例 1:P5443 [APIO2019] 桥梁
\(n\) 点 \(m\) 边的无向图,边有边权,\(q\) 次操作每次修改一条边的边权,或查询从某个点开始只经过边权不小于给定值的边一共可以到达多少点,\(n\le5\times10^4,m,q\le10^5\),\(2s,0.5G\)
将操作每 \(B\) 个分块,每次处理一块询问
将 \(O(B)\) 条在这 \(B\) 个操作中被修改的边取出,剩下的(称为原始边)按边权从大到小排序
将 \(O(B)\) 个询问按边权从大到小
依次扫描每个询问,将边权不小于当前询问边权的原始边加入并查集
对于被修改的边,分别求出在当前询问时它们的边权,并临时加入并查集
求出答案后撤销临时的修改
总时间复杂度 \(O(\frac qB m\log m+qB\log n)\),取 \(B=\sqrt {\frac{m\log m}{\log n}}\) 得时间复杂度为 \(O(q\sqrt{m\log m\log n})\)
还可以继续优化
每组询问中都对 \(m\) 条边进行排序,可以初始时将边排好序,每次查询时单独取出被影响的边即可,时间复杂度变为 \(O(\frac qB m+qB\log n)\),取 \(B=\sqrt{\frac{m}{\log n}}\) 得时间复杂度为 \(O(q\sqrt{m\log n})\)
发现不需要使用可撤销并查集,对于原始边,直接使用路径压缩的并查集即可,临时边不需要加入并查集,看做 \(O(B)\) 边的图后一次 \(dfs\) 即可,加上前面的优化,时间复杂度为 \(O(\frac qB m+qB\alpha(n))\),取 \(B=\sqrt{\frac{m}{\alpha(n)}}\) 得时间复杂度为 \(O(q\sqrt{m\alpha(n)})\),基本上可以看做带一定常数的 \(O(q\sqrt m)\) 了
例 2:P6578 [Ynoi2019] 魔法少女网站(第十分块)
给定 \(a_{1\sim n}\),单点修改,或查询某个区间有多少子区间的最大值不超过给定值,\(n,m\le3\times10^5,a_i\le n\)
方法类似,将 \(B\) 个放到一起处理
将询问按查询的值从小到大排序,并将原数组没有被修改的位置也从小到大排序
在扫描询问的同时,维护一个长度为 \(n\) 的 \(0/1\) 序列,表示这个位置的 \(a_x\) 是否不超过当前询问的值
对于未修改的部分,每次将 \(0/1\) 序列一个位置由 \(0\) 变为 \(1\)
对于修改的部分,枚举每个操作,求出到当前询问之前每个位置的值,由此计算是否设为 \(1\)
询问 \([l,r]\) 相当于在 \(0/1\) 序列的 \([l,r]\) 中查询有多少全 \(1\) 子序列
然后撤销后一部分的影响
上述操作可以用分块维护,单次修改和撤销 \(O(1)\),查询 \(O(\sqrt n)\)
总时间复杂度 \(O(\frac mBn\log n+mB)\),取 \(B=\sqrt{n\log n}\) 时时间复杂度为 \(O(m\sqrt{n\log n})\),建议块长取 \(5\sqrt{n\log n}\)
若将排序换为基排,或每次取出修改过的位置归并,则时间复杂度为 \(O(\frac mBn+mB))\),取 \(B=\sqrt n\) 得时间复杂度为 \(O(m\sqrt n)\)
时间分块维护区间操作
要求查询的信息可合并
询问离线,将序列分块,每次处理一个块
若部分修改则暴力重构块,部分查询则暴力实现,整块查询或修改则用一些方式维护
可以做 P4118 [Ynoi2018] 末日时在做什么?有没有空?可以来拯救吗?(第六分块)
树分块
以下只考虑维护链信息的树分块
树上撒点树分块
在树上随机选择 \(B\) 个关键点,则相邻关键点的距离期望为 \(O(\frac nB)\)
也可从下往上考虑,若存在一个非关键点,满足其 \(1\sim \frac nB\) 级祖先都不是关键点,则将其(选择合法的点中最深的一个)\(\frac nB\) 级祖先设为关键点,这样相邻关键点的距离为严格的 \(O(\frac nB)\)
将树从关键点处断开,分为若干块,块的大小无法保证,但块的直径一定不超过 \(O(\frac nB)\)
若要维护链的信息,则先求出每对相邻关键点之间的链的信息,然后由此求出每一对关键点之间的链的信息,查询一条链时,可以拆为不超过 \(O(\frac nB)\) 个散点和已经预处理的部分
若加上修改,则记录每个点到父亲关键点的链的修改标记,对于整段修改直接打标记,非整段修改则暴力重构链,由于块大小不保证,因此不能重构每个非关键点到父亲关键点的信息
例 1:P6177 Count on a tree II/【模板】树分块
在线链上数颜色,\(n\le4\times10^4,q\le10^5,2s,0.5G\)
对颜色离散化,然后预处理关键点之间的 bitset,保存是否出现了每种颜色
查询时 bitset 或起来即可
时间复杂度 \(O(\frac{n^2}\omega+q\sqrt n)\),空间复杂度 \(O(\frac{n^2}\omega)\),需要卡空间
有时间复杂度 \(O(q\sqrt n)\),空间复杂度 \(O(n\sqrt n)\) 的做法,但比较复杂且不一定比 bitset 快
真正 树上莫队
链询问,以一端所在块编号为第一关键字,另一端的 \(dfn\) 为第二关键字排序
将块按 \(dfn\) 编号,则左端点同一块内的询问,右端点总移动距离不超过 \(O(n)\),左端点单次移动距离不超过块的直径,因此使用上述树分块就可以了
写起来需要注意许多细节
王室联邦 树分块
顾名思义,P2325 [SCOI2005] 王室联邦 的树分块方法,保证块的大小和数量,不保证块连通,但是块中所有点并上块中所有点的 \(lca\) 是连通的
vjudge 练习
sqrt,pw:polylogisbetterthansqrt

浙公网安备 33010602011771号