分块入门 LibreOJ分块九题

推荐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 }
View Code

分块九题二:区间加法,查询小于某值的元素个数

分块九题三:区间加法,查询前驱

分块九题四:区间加法,区间求和

分块九题五:区间开方,区间求和

分块九题六:单点插入,单点查询

分块九题七:区间乘法和加法,区间查询

分块九题八:区间查询,区间修改

分块九题九:区间最小众数

猜你喜欢

转载自www.cnblogs.com/QAQorz/p/9356208.html