zoukankan      html  css  js  c++  java
  • 分块入门 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

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

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

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

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

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

  • 相关阅读:
    大话设计模式Python实现-代理模式
    大话设计模式Python实现-装饰模式
    大话设计模式Python实现-策略模式
    设计模式Python实现-简单工厂模式
    Python文件读写机制
    python 多线程剖析
    I/O多路复用-EPOLL探索
    Python学习笔记:魔术方法详解
    Django学习笔记:为Model添加Action
    【Django】Django Debug Toolbar调试工具配置
  • 原文地址:https://www.cnblogs.com/QAQorz/p/9356208.html
Copyright © 2011-2022 走看看