数列分块!!!
世界上怎么会有分块那么可爱的思想!!!
一、概念
分块,你可以说它是数据结构,也可以说是一种思想。
而数列分块呢,就是将一段序列分成许多块,分别维护每块的信息,来求出一段区间的信息(比如最大值,区间和等等)。
可以用下面的图片来理解:
Emm,搞错了:
对于区间询问 + 修改的问题,我们就可以使用分块的思想,在区间覆盖的整块部分直接修改整块的信息,而不完整块直接暴力处理(太可爱了,真的很好写!)
设块长为
二、写法
1.处理每块左右端点
for(int i = 1;i <= n;i ++){
bel[i] = (i - 1) / len + 1;//bel[i]表示属于那个块,len为块长
}
for(int i = 1;i <= num;i ++){
l[i] = (i - 1) * s + 1;
r[i] = i * s;
}//l[i] 为一个区间的左端点,r[i]为一个区间的右端点,num为块的数量
r[num] = n;
// 注意,最后一块可能不完整!
2.修改操作
- 区间修改
和线段树类似,分块也可以有 lazy_tag,对于直接修改整块,可以直接打 Tag,当查询 or 修改到该块的部分时再下放标记。
区间修改一般是这么一个框架:
if(bel[l] == bel[r]){
pushdown(bel[l]);
暴力执行
}else{
pushdown(bel[l]);
for(int i = l;i <= R[bel[l];i ++){
暴力
}
pushdown(bel[r]);
for(int i = L[bel[r]] ;i <= r;i ++){
暴力
}
for(int i = bel[l] + 1;i <= bel[r] - 1;i ++){
块信息整体修改
}
}
- 单点修改
直接暴力修改单点,再对所属块,重新记录信息。
Link:
a[x] = ···;
for(int i = L[bel[x]] ;i <= R[bel[x]];i ++){
val[bel[x]] = ···;
}
3.查询操作
- 区间查询
区间查询与区间修改差不了太多:
if(bel[l] == bel[r]){
pushdown (bel[l]);
暴力统计
}else{
pushdown (bel[l]);
for(int i = l;i <= R[bel[l];i ++){
暴力统计
}
pushdown (bel[r]);
for(int i = L[bel[r]] ;i <= r;i ++){
暴力统计
}
for(int i = bel[l] + 1;i <= bel[r] - 1;i ++){
块信息整体统计
}
}
- 单点查询
先将所属块标记下放,直接获取
Link:
pushdown(bel[x]);
ans = a[x];
二、常见错误
-
在最后一块的右端点没有设为
-
在查询时忘记下放标记
-
标记改变错误
三、技巧 / 套路
- 对于种类数的统计,由于块的数量只有
,于是乎可以直接开下 的数组,直接统计。 link: P3313 [SDOI2014] 旅行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)