推荐hzwer大佬的博客:http://hzwer.com/8053.html
初步学习体验就是,分块作为一种复杂度看上去并不优秀实际效率却很优秀的数据结构,在解决某些用其他如线段树等log数据结构非常麻烦的问题时非常好写好用,以后搭配莫队简直是解决大部分区间问题的强力算法
分块本身其实还是挺简单的,这套题总结了一些分块/数据结构的经典问题,很全面,能由浅入深地学习分块的基本使用,分块的特点。切了这几道板子题,分块至少终于不是什么遥不可及的神仙操作了
分块九题一:区间加法,单点查询
这题也作为线段树,树状数组之类的入门题
分块的思想大致都了解了是小块暴力大块更新,究竟是如何做到的,与线段树等有什么相似,这里就以第一题为例介绍分块基本操作
sz = sqrt(n);
sz指定了块的大小为根号n,这里是一个全局变量
b[i] = (i-1)/sz+1;
b数组(belong)表示坐标为i的点属于哪个块,显然这是为了后面直接访问块的编号
区间加上c的add操作如下:
void add(int l, int r, int c){ for (int i = l; i <= min(r, b[l]*sz); i++) a[i] += c; //左端不完整块 if (b[l] != b[r]) for (int i = (b[r]-1)*sz+1; i <= r; i++) a[i] += c; //右端不完整块 for (int i = b[l]+1; i <= b[r]-1; i++) atag[i] += c; //完整块 }
分块的更新与查询基本就是这种模板,先暴力左右两边(要特判左右在不在一个块)然后用一个atag数组给区间打上标记
atag标记类似于线段树的lazy标记,是对一个块(区间)操作的值
那么,更新之后某一点i的结果就是
printf("%d\n", a[i]+atag[b[i]]);
第一题的完整代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 7 const int mx = 50010; 8 int sz; 9 int a[mx], b[mx], atag[mx]; 10 11 void add(int l, int r, int c){ 12 for (int i = l; i <= min(r, b[l]*sz); i++) a[i] += c; //左端不完整块 13 if (b[l] != b[r]) 14 for (int i = (b[r]-1)*sz+1; i <= r; i++) a[i] += c; //右端不完整块 15 for (int i = b[l]+1; i <= b[r]-1; i++) atag[i] += c; //完整块 16 } 17 18 int main(){ 19 int n, op, l, r, c; 20 while (scanf("%d", &n) == 1){ 21 sz = sqrt(n); 22 for (int i = 1; i <= n; i++){ 23 scanf("%d", &a[i]); 24 b[i] = (i-1)/sz+1; 25 atag[i] = 0; 26 } 27 for (int i = 1; i <= n; i++){ 28 scanf("%d%d%d%d", &op, &l, &r, &c); 29 if (!op) add(l, r, c); 30 if (op == 1) printf("%d\n", a[r]+atag[b[r]]); 31 } 32 } 33 return 0; 34 }
分块九题二:区间加法,查询小于某值的元素个数
分块九题三:区间加法,查询前驱
分块九题四:区间加法,区间求和
分块九题五:区间开方,区间求和
分块九题六:单点插入,单点查询
分块九题七:区间乘法和加法,区间查询
分块九题八:区间查询,区间修改
分块九题九:区间最小众数