专栏文章
莫队与分块
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miql5f46
- 此快照首次捕获于
- 2025/12/04 06:35 3 个月前
- 此快照最后确认于
- 2025/12/04 06:35 3 个月前
P2709 小B的询问
-
给定一个长度为 的整数序列 ,值域 。有 个询问,每次给定一个区间 ,求 , 表示数字 在 中的出现次数。
-
,,
-
莫队模板题。
-
将原序列分成若干块,每块 块长 为 ,把所有询问 离线 下来,以左端点 所在块的编号 为第一关键字,右端点 的大小为第二关键字排序。
-
考虑维护指针 和 分别表示当前区间的左右端点,用 和 分别表示当前询问区间的左右端点
-
-
若有 则需要让 向左移动:
i --,后删除cnt[a[i]] --,在删除的同时,更新一下当前的答案ans。 -
,, 的情况同理。
-
-
一个小结论:若需要添加 (
add),则一定是先移动指针,后添加;若删除 (del),则一定是先删除,后移动指针。 -
一个小细节:初始时,把 ,,表示当前区间为空,还没有添加任何元素。
-
复杂度分析:
-
对于 的移动:
-
-
若两次询问的左端点 在一个块内,则两个询问之间最多需要 移动 次, 个询问最多 次
-
若两次询问左端点 所在的块变化,考虑分析最坏情况,画个图即可发现 移动距离不会超过 。因此复杂度忽略不计。
-
-
对于 移动:
-
-
若 移动时,两次询问的左端点 始终在一个块内,由于右端点 单调,每个块 内 移动次数 总共 不超过 , 个块,则 移动次数不超过
-
若 移动时,两次询问的左端点 所在块发生了变化。 个块,则最多发生 次这样的变化,每次 的变化最多是 ,则 的移动最多是
-
-
由于 太大时, 移动的复杂度过大; 太小时, 移动复杂度过大。考虑找到一个平衡点,令 ,解得 。因此最终复杂度为
-
trick:当块编号是奇数时,按照 递增排序;编号是偶数时,按照 递减排序。
P1494 小 Z 的袜子
-
给定 个袜子,每一个袜子都有一个颜色
-
给定 组询问,每次给定一个区间 ,问从区间 随机抽出两只颜色相同袜子的概率。
-
, ,
-
概率为 , 表示颜色 在区间 出现的次数。
-
用莫队维护 ,以及 即可。
P4462 [CQOI2018] 异或序列
给定一个长度为 的序列 和一个参数 有 组询问,每次给定两个参数 ,问有多少个点对 满足 ,并且 ,,
-
首先 的异或和可以用前缀异或和 来表示
-
莫队,考虑指针 和 移动时顺便更新答案。设 表示 中 的个数, 表示 中 的个数。
-
注意,移动指针时, 和 都需要更新。并且 要注意更新 数组和更新答案的先后顺序!
loj 6277
-
给定一个长度为 的序列 。
-
个操作,每个操作都有三个参数 ,,,
-
,保证答案在
int范围内。 -
-
若 ,把区间 的每一个数都加上
-
若 ,查询 的值
-
-
对数列分块。
build时算出p[i],bl[i],br[i],分别表示 所在的块的编号, 所在块的左端点的编号,以及右端点的编号。 -
修改时,先检查是否 和 在同一个块(即
p[l] = p[r])。若在一个块,则暴力即可;若不在,给中间的整块打上加法标记,散块暴力即可。单次修改复杂度 -
查询时,直接让 和其所在块的加法标记相加即可。
-
令 ,得 ,因此总复杂度 , 表示询问次数
loj 6278
-
给定一个长度为 的序列
-
个操作,每个操作都有四个参数 ,,,
-
-
若 ,把区间 的数都加上
-
若 ,查询区间 小于 的数的个数。
-
-
,保证答案在
int范围内。 -
对数列分块,设块长为 。对每一个块维护一个递增的数列,以及区间的加法标记。
-
查询时,对于整块二分即可;对于散块直接暴力枚举即可。注意,由于区间有加法标记,所以要找的是这个块的递增数列第一个小于 的位置。 表示 所在块的加法标记。单次查询复杂度
-
修改时,对于整块,打上加法标记即可,并且维护的递增数列的单调性不会被破坏。对于散块,暴力给每个 加上 。由于破坏了散块递增数列的单调性,所以需要给这个块重新排序。单次修改复杂度 。
-
我们希望查询和修改复杂度能够相等,令 ,解得 ,因此总复杂度
loj 6279
-
给定一个长度为 的序列
-
个操作,每个操作都有四个参数 ,,,
-
-
若 ,把区间 的数都加上
-
若 ,查询区间 小于 的数的最大值。
-
-
,保证答案在
int范围内。 -
思路和上一题类似。
loj 6280
-
给定一个长度为 的序列
-
个操作,每个操作都有四个参数 ,,,
-
-
若 ,把区间 的数都加上
-
若 ,输出 对 取模后的答案。
-
-
,不保证答案在
int范围内。 -
对数列分块,设块长为 。对于每一个块,维护区间加法标记 以及区间和
sum[k], 表示这个块内的每个元素需要都加上 ,但现在还没有加。 -
修改时,对于整块,更新
tag[k]以及sum[k]即可。对于散块,暴力更新a[i]和sum[k]即可,tag[k]无需更新。单次修改复杂度 -
查询时,对于整块,直接查询
sum[k]即可。对于散块,暴力累加a[i] + tag[k]即可。单次查询 -
令 ,则单次查询和修改复杂度均为 ,因此总复杂度 , 表示操作次数。
loj 6281
-
给定一个长度为 的序列
-
个操作,每个操作都有四个参数 ,,,
-
-
若 ,对区间 中的每一个数 执行
-
若 ,输出 。
-
-
,保证答案在
int范围内。 -
将数列分块,设块长为 。维护块内区间最大值
mx[k],以及块内区间和sum[k] -
修改时:
-
-
对于散块,暴力给 开根号,更新块内最大值即可,复杂度为 。
-
对于整块,若块内区间最大值
mx[k],则暴力给整个块的 开根号。枚举整块复杂度为 。对于每一个整块,最多开 次根号,因此给整块暴力开根号的 总复杂度 为 ,可忽略不计。 -
因此,可以认为单次修改复杂度为
-
-
查询时,对于散块,暴力加 即可,复杂度 。对于整块,直接加区间和
sum[k]即可,复杂度 。因此单次查询复杂度为 -
令 ,则总复杂度为 , 为操作次数
loj 6282
-
给定一个长度为 的数列。
-
有 个操作,每次给定四个参数 ,,,
-
-
若 ,表示在第 个数字前插入数字
-
若 ,表示查询 的值。
-
-
根号重构
-
初始时,对数列分块,块长为 。
-
对于每一个块维护一个数组
b[k][x],表示编号为 的块的数组的第 个元素,插入时,从小到大枚举块的编号,找到对应的块,插入即可。单次插入复杂度为块的个数对应的块的大小。 -
由于频繁的插入会导致某一个块的大小过大,最坏有可能是 级别的。因此每隔 次插入,我们就重构一次,这样可以保证所有块的大小不超过 。
-
插入复杂度为 ,重构的总复杂度为
-
查询时,依旧从小到大枚举块的编号,找到对应的块即可。查询复杂度为
-
总复杂度
牛客多校 Distance
-
给定一个 的空间。
-
有 次操作,每次给定四个参数 ,,,。
-
若 ,表示给点 打上标记
-
若 ,表示查询 到已经标记的点的曼哈顿距离的最小值。
-
,
-
根号重构。令
-
考虑暴力怎么做:把被标记的点塞到一个集合(
vector) 里面,每次查询的时候暴力枚举集合内的点。 -
但是我们不希望每次都枚举集合内所有点。
-
注意到不管关键点有多少个,把每一个关键点作为起点,跑一遍多源
bfs,都可以在 的时间复杂度内算出任意一个点到关键点的最小值。 -
考虑根号重构。每新标记 个点后就重新跑一遍多源
bfs,发现跑完bfs之后需要枚举的点不超过 个。因此复杂度为 ,令 ,总复杂度为
loj 6283
-
给定一个长度为 的序列
-
个操作,每次操作给定四个参数 ,,,
-
-
若 ,表示给 加上
-
若 ,表示给 乘上
-
若 ,表示输出 对 取模后的结果
-
-
,答案保证无需开
long long -
对数列分块,块长为 。对每一个块维护乘法标记 和加法标记
-
修改时,若是整块:
-
-
如果是区间加法,则直接让加法标记加上 即可
-
如果是区间乘法,则需要让乘法标记、加法标记都乘上
-
-
若是散块:一定要先下放标记(让散块内的每一个数都乘上 然后加上 ),然后再暴力修改。
loj 6285
-
给定一个长度为 的序列
-
有 组询问,每次给定两个参数 ,,表示询问区间最小众数。
-
,
-
对序列分块,块长为 。预处理
ans[i][j]表示编号在 的块的最小众数,以及cnt[i][j]表示前 个整块内数字 出现的次数。预处理时,第一层枚举最左边块的编号 ,第二层枚举最右边的编号 ,第三层枚举第 块的所有数字,加入桶中,统计答案。复杂度 -
查询时,先让答案(最小众数)继承整块的答案
ans[i][j],然后枚举散块内的元素,加入桶中,某一个数 此时此刻出现次数为在整块出现次数cnt[r][x] - cnt[l - 1][x](l和r分别是最左边整块和最右边整块的编号),加上在散块中出现次数bucket[x],尝试用二者之和更新答案即可。
P2617 Dynamic Rankings
给定一个长度为 的序列 有 次操作,操作分两种: 查询区间 第 小值。 单点修改,把 ,
-
算是动态二维数点吧, 算第一维,第 小算第二维。
-
查询时,跳块即可。跳块复杂度 ,为了不让复杂度退化成 ,考虑根号平衡,设 表示值域在第 块,并且序列下标在 块的个数, 表示值域为 ,并且序列下标在 块的个数。这样就算出了 值域上的整块,序列上的整块 的个数,以及 值域上的散块,序列上的整块 的个数。
-
对于 (值域上整块,序列上散块),可以先把序列上的散块的数 对应的值域上块的编号 装到桶里。对于(值域上散块,序列上散块)的个数,可以先把序列上的散块的数 加入桶里。
牛客多校第 6 场 E Array
给定一个长度为 的序列,初始所有元素为 给定一个长度为 的序列 ,满足 ,有 次操作吗,操作分两种: 给定参数 ,,,给 加上 给定参数 ,,问 ,
-
把问题放到二维平面上来看, 表示第 列 行,查询相当于查 所有列的点权之和。修改相当于给 行所有点的点权加上
-
考虑把列分块,每 个分成一组。查询的时候可以查 整组之和 以及单独的列(即 )
-
修改时可以直接考虑对 查询的整组 的贡献,对于每一组,把组内元素按照 排序,通过二分,可得有多少个组内元素的列 在 之内(记作 ),则本次修改对该组的贡献为
-
修改时,对于零散的行,以及整块的行,考虑其对零散的列的贡献。其实,就是等价于:区间 ,每次查 次某个具体的位置 的值。把行分块,根号平衡即可。
-
另一种做法是把所有点按照 排序,然后每 个分一组,这样,最多只有 个零散的点需要暴力修改。查询时,考虑对列分块,块长为
-
首先考虑 修改 对 查询时整块 的贡献。设 表示第 组整体 后对第 个整块列的贡献, 表示第 组整体被加的标记。则查询时需要 枚举所有整组,需要 知道一个整组对一些整块的贡献,把 的第二维前缀和即可。散点 对整块的贡献设一个数组即可。
-
修改 对 查询时散列 的贡献,可以和上个做法一样,用一个分块,也可以分别考虑修改时零散的点对查询时零散的列、以及整块的贡献。
-
注意
vector的一些细节:对ord排序时,不能是单纯地sort(ord.begin() , ord.end()),因为定义固定长度的vector时,初始里面的元素都是
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...