分块入门 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
", 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
", a[r]+atag[b[r]]);
31         }
32     }
33     return 0;
34 }
View Code

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

小块暴力 大块标记 整块排序 及时重构

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <vector>
 7 #define LL long long
 8 #define INF 0x3f3f3f3f
 9 #define debug(x) cout << #x << " = " << x << endl;
10 using namespace std;
11 
12 const int mx = 10010;
13 int a[mx*5], b[mx*5];
14 int atag[mx];
15 vector<int> G[mx];
16 int n, sz;
17 
18 void reset(int x){
19     G[x].clear();
20     for (int i = (x-1)*sz+1; i <= min(n, x*sz); i++)
21         G[x].push_back(a[i]);
22     sort(G[x].begin(), G[x].end());
23 }
24 
25 void add(int l, int r, int x){
26     for (int i = l; i <= min(r, sz*b[l]); i++) a[i] += x;
27     reset(b[l]);
28     if (b[l] != b[r]) {
29         for (int i = (b[r] - 1) * sz + 1; i <= r; i++) a[i] += x;
30         reset(b[r]);
31     }
32     for (int i = b[l]+1; i <= b[r]-1; i++) atag[i] += x;
33 }
34 
35 int query(int l, int r, int c){
36     int ans = 0;
37     for (int i = l; i <= min(r, sz*b[l]); i++)
38         if (a[i] + atag[b[l]] < c) ans++;
39     if (b[l] != b[r]){
40         for (int i = (b[r]-1)*sz+1; i <= r; i++)
41             if (a[i] + atag[b[r]] < c) ans++;
42     }
43     for (int i = b[l]+1; i <= b[r]-1; i++){
44         int k = lower_bound(G[i].begin(), G[i].end(), c-atag[i])-G[i].begin();
45         ans += k;
46     }
47     return ans;
48 }
49 
50 int main(){
51     scanf("%d", &n);
52     sz = sqrt(n);
53     for (int i = 1; i <= n; i++) {
54         scanf("%d", &a[i]);
55         b[i] = (i - 1) / sz + 1;
56         G[b[i]].push_back(a[i]);
57     }
58     for (int i = 1; i <= b[n]; i++)
59         sort(G[i].begin(), G[i].end());
60     int op, l, r, x;
61     for (int i = 1; i <= n; i++) {
62         scanf("%d%d%d%d", &op, &l, &r, &x);
63         if (!op) add(l, r, x);
64         else printf("%d
", query(l, r, x * x));
65     }
66     return 0;
67 }
View Code

  

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

每块用一个set维护 二分找最大前驱 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <set>
 7 #define LL long long
 8 #define INF 0x3f3f3f3f
 9 #define debug(x) cout << #x << " = " << x << endl;
10 using namespace std;
11 
12 const int mx = 1e5+7;
13 int a[mx], b[mx];
14 int atag[1010];
15 set<int> s[1010];
16 int n, sz;
17 
18 void add(int l, int r, int c){
19     for (int i = l; i <= min(r, sz*b[l]); i++){
20         s[b[l]].erase(a[i]);
21         a[i] += c;
22         s[b[l]].insert(a[i]);
23     }
24     if (b[l] != b[r]){
25         for (int i = (b[r]-1)*sz+1; i <= r; i++){
26             s[b[r]].erase(a[i]);
27             a[i] += c;
28             s[b[r]].insert(a[i]);
29         }
30     }
31     for (int i = b[l]+1; i <= b[r]-1; i++) atag[i] += c;
32 }
33 
34 int query(int l, int r, int c){
35     int ans = -1;
36     for (int i = l; i <= min(r, sz*b[l]); i++){
37         int v = a[i] + atag[b[l]];
38         if (v < c) ans = max(ans, v);
39     }
40     if (b[l] != b[r]){
41         for (int i = (b[r]-1)*sz+1; i <= r; i++){
42             int v = a[i] + atag[b[r]];
43             if (v < c) ans = max(ans, v);
44         }
45     }
46     for (int i = b[l]+1; i <= b[r]-1; i++){
47         int v = c-atag[i];
48         auto it = s[i].lower_bound(v);
49         if (it == s[i].begin()) continue;
50         --it;
51         ans = max(ans, *it + atag[i]);
52     }
53     return ans;
54 }
55 
56 int main(){
57     scanf("%d", &n);
58     sz = sqrt(n);
59     for (int i = 1; i <= n; i++){
60         scanf("%d", &a[i]);
61         b[i] = (i-1)/sz+1;
62         s[b[i]].insert(a[i]);
63     }
64     int op, l, r, c;
65     for (int i = 1; i <= n; i++){
66         scanf("%d%d%d%d", &op, &l, &r, &c);
67         if (!op) add(l, r, c);
68         else printf("%d
", query(l, r, c));
69     }
70     return 0;
71 }
View Code

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

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <set>
 7 #define LL long long
 8 #define INF 0x3f3f3f3f
 9 #define debug(x) cout << #x << " = " << x << endl;
10 using namespace std;
11 
12 const int mx = 50010;
13 LL a[mx];
14 int b[mx];
15 LL atag[1010], sum[1010];
16 int n, sz;
17 
18 void add(int l, int r, int c){
19     for (int i = l; i <= min(r, sz*b[l]); i++){
20         a[i] += c;
21         sum[b[l]] += c;
22     }
23     if (b[l] != b[r]){
24         for (int i = (b[r]-1)*sz+1; i <= r; i++){
25             a[i] += c;
26             sum[b[r]] += c;
27         }
28     }
29     for (int i = b[l]+1; i <= b[r]-1; i++) atag[i] += c;
30 }
31 
32 LL query(int l, int r, int c){
33     LL ans = 0;
34     for (int i = l; i <= min(r, sz*b[l]); i++)
35         ans = (ans + a[i] + atag[b[l]]) % c;
36     if (b[l] != b[r]){
37         for (int i = (b[r]-1)*sz+1; i <= r; i++)
38             ans = (ans + a[i] + atag[b[r]]) % c;
39     }
40     for (int i = b[l]+1; i <= b[r]-1; i++)
41         ans = (ans + sum[i] + atag[i]*sz) % c;
42     return ans;
43 }
44 
45 int main(){
46     scanf("%d", &n);
47     sz = sqrt(n);
48     for (int i = 1; i <= n; i++){
49         scanf("%lld", &a[i]);
50         b[i] = (i-1)/sz+1;
51         sum[b[i]] += a[i];
52     }
53     int op, l, r, c;
54     for (int i = 1; i <= n; i++){
55         scanf("%d%d%d%d", &op, &l, &r, &c);
56         if (!op) add(l, r, c);
57         else printf("%lld
", query(l, r, c+1));
58     }
59     return 0;
60 }
View Code

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

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

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

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

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

原文地址:https://www.cnblogs.com/QAQorz/p/9356208.html