数列分块!!!

世界上怎么会有分块那么可爱的思想!!!

一、概念

分块,你可以说它是数据结构,也可以说是一种思想。

而数列分块呢,就是将一段序列分成许多块,分别维护每块的信息,来求出一段区间的信息(比如最大值,区间和等等)。

可以用下面的图片来理解:

Emm,搞错了:

对于区间询问 + 修改的问题,我们就可以使用分块的思想,在区间覆盖的整块部分直接修改整块的信息,而不完整块直接暴力处理(太可爱了,真的很好写!)

设块长为 b,该算法时间复杂度一般为:O(B+NB),当 B=N 时取得最小。

二、写法

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 ++){
        块信息整体统计
    }
}
  • 单点查询

先将所属块标记下放,直接获取 Ans

Link:

pushdown(bel[x]);
ans = a[x];

二、常见错误

  • 在最后一块的右端点没有设为 n

  • 在查询时忘记下放标记

  • 标记改变错误

三、技巧 / 套路

  • 对于种类数的统计,由于块的数量只有 N,于是乎可以直接开下 cnt[x][val] 的数组,直接统计。 link: P3313 [SDOI2014] 旅行
posted @   固态H2O  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示