• 分块入门


    版权申明:本文转自 Monica

    首先,分块是什么意思呢,顾名思义就是把要处理的东西进行分块,分成一块一块的233,举个很简单的例子,对于一个数列 size(a{ })=5,我们可以把前2个分到一起,再两个分到一起,最后单下来一个,为什么要这样处理呢?这样处理的好处又是什么呢?
    我们也可以这样思考,如果我们把一个数列,当该数列的长度为n的时候,我们以根号n为一段,分出来的段数不超过根号n,如果我们要进行区间的处理,比如加法减法等,可以对于修改区间[ L , R ]可以把其中框起来的块(一块是根号n的大小)直接打上标记,由于每一块的长度不大于根号n,所以对于两边没有框起来的部分,我们直接暴力地进行更新,这样操作次数是最多2倍根号n的,而中间的标记是O(1)处理的,这就是为什么该类算法是根号级别的原因


    思路看起来很清晰吧,好像很简单的样子,那么我们马上就来试试吧


    以黄学长的分块学习顺序为例
    (本文的所有题目,均来自黄学长,www.hzwer.com)

    我们先进行区间修改单点查询操作

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<cmath>
     5 #define MAXN 100000
     6 using namespace std;
     7 int a[MAXN],add[MAXN],b[MAXN],len,n,m;//add为标记
     8 void modify(int l,int r,int ad){
     9     for(register int i=l;i<=min(b[l]*len,r);i++) a[i]+=ad;
    10     if(b[l]!=b[r]){//注意此处的min操作,可能会在小数据卡到你
    11         for(register int i=(b[r]-1)*len+1;i<=r;i++) a[i]+=ad; 
    12     }
    13     for(register int i=b[l]+1;i<=b[r]-1;i++)add[i]+=ad;
    14 }
    15 int main(){
    16     scanf("%d%d",&n,&m);
    17     len=sqrt(n);
    18     for(register int i=1;i<=n;i++)scanf("%d",&a[i]);
    19     for(register int i=1;i<=n;i++)b[i]=(i-1)/len+1;
    20     for(register int i=1;i<=m;i++){
    21         int temp;
    22         scanf("%d",&temp);
    23         if(temp==1){
    24             int l,r,ad;
    25             scanf("%d%d%d",&l,&r,&ad);
    26             modify(l,r,ad);
    27         }else{
    28             int loc;
    29             scanf("%d",&loc);
    30             printf("%d
    ",a[loc]+add[b[loc]]);
    31         }//该点的值和该店所在的区间累加的标记
    32     }
    33     return 0;
    34 }

    接下来是区间修改,区间查询小于某值的数有多少个

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cmath> 
    #define MAXN 1000000+10
    #define MINN 5000
    using namespace std;
    vector<int>v[MINN];
    int len,a[MAXN],b[MAXN],add[MINN];
    void reset(int zone){
        v[zone].clear();
        for(register int i=(zone-1)*len+1;i<=zone*len;i++) v[zone].push_back(a[i]);
        sort(v[zone].begin(),v[zone].end());
    }
    void modify(int l,int r,int ad){
        for(register int i=l;i<=min(b[l]*len,r);i++) a[i]+=ad;
        reset(b[l]);
        if(b[l]!=b[r]){
            for(register int i=(b[r]-1)*len+1;i<=r;i++) a[i]+=ad;
            reset(b[r]);
        }
        for(register int i=b[l]+1;i<=b[r]-1;i++) add[i]+=ad;
    }
    int query(int l,int r,int k){
        int cnt=0;
        for(register int i=l;i<=min(b[l]*len,r);i++){
            if(a[i]+add[b[i]]<k)cnt++;
        }
        if(b[l]!=b[r]){
            for(register int i=(b[r]-1)*len+1;i<=r;i++){
                if(a[i]+add[b[i]]<k)cnt++;
            }
        }
        for(register int i=b[l]+1;i<=b[r]-1;i++){
            int x=k-add[i];
            cnt+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
        }
        return cnt;
    }
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        len=sqrt(n);
        for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(register int i=1;i<=n;i++){
            b[i]=(i-1)/len+1;
            v[b[i]].push_back(a[i]);
        }
        for(register int i=1;i<=b[n];i++) sort(v[i].begin(),v[i].end());
        for(register int i=1;i<=m;i++){
            int temp;
            scanf("%d",&temp);
            if(temp==1){
                int l,r,ad;
                scanf("%d%d%d",&l,&r,&ad);
                modify(l,r,ad);
            }else{
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                printf("%d
    ",query(l,r,k));
            }
        }
        return 0;
    }

    接着是第三个问题

    区间修改,查区间内某个数的前驱,如果没有则返回0

    这个问题和上个问题类似,只不过由于需要查前驱,vector不能做到(只能upper _ bound和lower _ bound),而vector是支持元素可重的,这样一来无法得知其前驱(因为lower _ bound是返回第一个大于他的元素的迭代器,upper _ bound是返回值第一个大于他并且在可重范围内的最后一个数),所以只需把数据结构改成set就好了,因为set是不可重集

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<set>
     5 #include<cmath> 
     6 #define MAXN 100000
     7 #define MINN 1000
     8 using namespace std;
     9 int len,n,m;
    10 set<int>s[MINN];
    11 int add[MINN];
    12 int a[MAXN],be[MAXN];
    13 void reset(int loc){
    14     s[loc].clear();
    15     for(register int i=(loc-1)*len+1;i<=loc*len;i++) s[loc].insert(a[i]); 
    16 }
    17 void modify(int from,int to,int ad){
    18     for(register int i=from;i<=min(to,be[from]*len);i++) a[i]+=ad;
    19     reset(be[from]);
    20     if(be[from]!=be[to]){
    21         for(register int i=(be[to]-1)*len+1;i<=to;i++) a[i]+=ad;
    22         reset(be[to]);
    23     }
    24     for(register int i=be[from]+1;i<=be[to]-1;i++) add[i]+=ad;
    25 }
    26 int query(int from,int to,int k){
    27     int cnt=0;
    28     for(register int i=from;i<=min(be[from]*len,to);i++)if(a[i]<k)cnt=max(cnt,a[i]);
    29     if(be[from]!=be[to]){
    30         for(register int i=(be[to]-1)*len+1;i<=to;i++) if(a[i]<k)cnt=max(cnt,a[i]);
    31     }
    32     for(register int i=be[from]+1;i<=be[to]-1;i++){
    33         int x=k-add[i];
    34         set<int>::iterator loc=s[i].lower_bound(x);
    35         if(loc==s[i].begin()) continue;
    36         loc--;
    37         cnt=max(cnt,*loc+add[i]);
    38     }
    39     return cnt;
    40 }
    41 int main(){
    42     //freopen(".txt","r",stdin);
    43     //freopen(".out","w",stdout);
    44     scanf("%d%d",&n,&m);
    45     len=sqrt(n);
    46     for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
    47     for(register int i=1;i<=n;i++){
    48         be[i]=(i-1)/len+1;
    49         s[be[i]].insert(a[i]);
    50     }
    51     for(register int i=1;i<=m;i++){
    52         int temp;
    53         scanf("%d",&temp);
    54         if(temp==1){
    55             int l,r,ad;
    56             scanf("%d%d%d",&l,&r,&ad);
    57             modify(l,r,ad);
    58         }else{
    59             int l,r,k;
    60             scanf("%d%d%d",&l,&r,&k);
    61             printf("%d
    ",query(l,r,k));//0表示没有 
    62         }
    63     }
    64     return 0;
    65 }
    66 /*
    67 10 5
    68 1 2 3 4 5 6 7 8 9 10
    69 2 1 9 9
    70 1 1 3 2
    71 2 1 4 5
    72 */

    区间加法区间求和

    和前面一样,打mark就行了

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<cmath>
     5 #define MAXN 100000
     6 #define MINN 1000 
     7 using namespace std;
     8 int len;
     9 int n,m;
    10 int a[MAXN],add[MINN],val[MINN],be[MAXN];
    11 void modify(int from,int to,int ad){
    12     for(register int i=from;i<=min(to,be[from]*len);i++) a[i]+=ad,val[be[from]]+=ad;
    13     if(be[from]!=be[to]){
    14         for(register int i=(be[to]-1)*len+1;i<=to;i++) a[i]+=ad,val[be[to]]+=ad;
    15     }
    16     for(register int i=be[from]+1;i<=be[to]-1;i++) add[i]+=ad;
    17 }
    18 int query(int from,int to){
    19     int cnt=0;
    20     for(register int i=from;i<=min(to,be[from]*len);i++) cnt+=(a[i]+add[be[from]]);
    21     if(be[from]!=be[to]){
    22         for(register int i=(be[to]-1)*len+1;i<=to;i++) cnt+=(a[i]+add[be[to]]);
    23     }
    24     for(register int i=be[from]+1;i<=be[to]-1;i++) cnt+=(val[i]+add[i]*len);
    25     return cnt;
    26 }
    27 int main(){
    28     //freopen(".txt","r",stdin);
    29     //freopen(".out","w",stdout);
    30     scanf("%d%d",&n,&m);
    31     len=sqrt(n);
    32     for(register int i=1;i<=n;i++)be[i]=(i-1)/len+1;
    33     for(register int i=1;i<=n;i++)scanf("%d",&a[i]),val[be[i]]+=a[i];
    34     for(register int i=1;i<=m;i++){
    35         int temp;
    36         scanf("%d",&temp);
    37         if(temp==1){
    38             int f,t,ad;
    39             scanf("%d%d%d",&f,&t,&ad);
    40             modify(f,t,ad);
    41         }else{
    42             int f,t;
    43             cin>>f>>t;
    44             printf("%d
    ",query(f,t));
    45         }
    46     }
    47     return 0;
    48 }

    区间开方,区间求和

    我们可以考虑一个数,在保留int的情况下,只需要几次就会开方到1或者0,所以我们只需要记录一个块是否全部是0或者1就行了,因为再进行操作不会对其进行修改

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<cmath>
     5 #define MAXN 10000
     6 #define MINN 1000
     7 using namespace std;
     8 int n,m,be[MAXN],a[MAXN];
     9 int val[MINN];
    10 bool flag[MAXN];
    11 int len;
    12 void sqrtt(int zone){
    13     if(flag[zone]) return;
    14     flag[zone]=true;
    15     val[zone]=0;
    16     for(register int i=(zone-1)*len+1;i<=zone*len;i++){
    17         a[i]=sqrt(a[i]);
    18         val[zone]+=a[i];
    19         if(a[i]>1)flag[zone]=false;
    20     }
    21 }
    22 void modify(int from,int to){
    23     for(register int i=from;i<=min(to,be[from]*len);i++){
    24         val[be[from]]-=a[i];
    25         a[i]=sqrt(a[i]);
    26         val[be[from]]+=a[i];
    27     }
    28     if(be[from]!=be[to]){
    29         for(register int i=(be[to]-1)*len+1;i<=to;i++){
    30             val[be[to]]-=a[i];
    31             a[i]=sqrt(a[i]);
    32             val[be[to]]+=a[i];
    33         }
    34     } 
    35     for(register int i=be[from]+1;i<=be[to]-1;i++) sqrtt(i);
    36 }
    37 int query(int from,int to){
    38     int cnt=0;
    39     for(register int i=from;i<=min(to,be[from]*len);i++) cnt+=a[i];
    40     if(be[from]!=be[to]){
    41         for(register int i=(be[to]-1)*len+1;i<=to;i++) cnt+=a[i];
    42     }
    43     for(register int i=be[from]+1;i<=be[to]-1;i++) cnt+=val[i];
    44     return cnt;
    45 }
    46 int main(){
    47     //freopen("sqrt.txt","r",stdin);
    48     //ios::sync_with_stdio(false);
    49     cin>>n>>m;
    50     len=sqrt(n);
    51     for(register int i=1;i<=n;i++) be[i]=(i-1)/len+1; 
    52     for(register int i=1;i<=n;i++){
    53         scanf("%d",&a[i]);
    54         val[be[i]]+=a[i]; 
    55     }
    56     for(register int i=1;i<=m;i++){
    57         int temp;
    58         scanf("%d",&temp);
    59         if(temp==1){
    60             int f,t;
    61             cin>>f>>t;
    62             modify(f,t);
    63         }else{
    64             int f,t;
    65             cin>>f>>t;
    66             printf("%d
    ",query(f,t));
    67         }
    68     }
    69     return 0;
    70 }

    区间乘法,区间加法和区间查询

    其实这道题有一个更简单的做法,把乘法转化为加法,比如乘n可以想成加这个数的n-1倍,注意程序中mul(乘法)lazy数组和add(加法)lazy数组的转化

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<cmath>
     5 #define MAXN 100000
     6 #define MINN 1000
     7 using namespace std;
     8 
     9 int n,m,be[MAXN],a[MAXN];
    10 int val[MINN],mark[MINN];
    11 int len;
    12 void reset(int zone){
    13     if(mark[zone]==-1) return;
    14     for(register int i=(zone-1)*len+1;i<=zone*len;i++) a[i]=mark[zone];
    15     mark[zone]=-1;
    16 } 
    17 int solve(int from,int to,int c){
    18     int cnt=0;
    19     reset(be[from]);
    20     for(register int i=from;i<=min(to,be[from]*len);i++){
    21         if(a[i]==c) cnt++;
    22         else a[i]=c;
    23     }
    24     if(be[from]!=be[to]){
    25         reset(be[to]);
    26         for(register int i=(be[to]-1)*len+1;i<=to;i++){
    27             if(a[i]==c) cnt++;
    28             else a[i]=c;
    29         }
    30     }
    31     for(register int i=be[from]+1;i<=be[to]-1;i++){
    32         if(mark[i]!=-1){
    33             if(mark[i]==c){
    34                 cnt+=len;
    35             }else{
    36                 mark[i]=c;
    37             }
    38         }else{
    39             for(register int j=(i-1)*len+1;j<=i*len;j++){
    40                 if(a[j]==c) cnt++;
    41                 else a[j]=c;
    42             }
    43         }
    44     }
    45     return cnt;
    46 }
    47 int main(){
    48     //freopen("query.txt","r",stdin);
    49     cin>>n>>m;
    50     len=sqrt(n);
    51     memset(mark,-1,sizeof(mark));
    52     for(register int i=1;i<=n;i++) be[i]=(i-1)/len+1;
    53     for(register int i=1;i<=n;i++){
    54         scanf("%d",&a[i]);
    55         val[be[i]]+=a[i];
    56     }
    57     for(register int i=1;i<=m;i++){
    58         int f,t,c;
    59         scanf("%d%d%d",&f,&t,&c);
    60         printf("%d
    ",solve(f,t,c));
    61     }
    62     return 0;
    63 }
    64 /*
    65 5 6
    66 1 2 3 4 5
    67 1 3 2
    68 // 1->2 2 2 4 5
    69 1 4 2
    70 // 3->2 2 2 2 5
    71 1 2 3
    72 // 0->3 3 2 2 5
    73 1 5 2
    74 // 2->2 2 2 2 2
    75 1 2 4
    76 // 0->4 4 2 2 2
    77 2 5 4
    78 // 1-> 4 4 4 4 4
    79 */

    最后一个是求区间众数

    注意其中的lower_bound和upper _bound的操作,非常的巧妙,可以算出该区间该数为多少个,程序中的二元数组则是代表第i个块到第j个块的众数

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<vector>
     5 #include<cstring>
     6 #include<iostream>
     7 #include<algorithm>
     8 #include<map>
     9 #define MAXN 50000+10
    10 #define MINN 500+10
    11 using namespace std;
    12 map<int,int>m;
    13 vector<int>v[MAXN];
    14 int val[MAXN];
    15 int len,n,mm;
    16 int cnt[MINN][MINN];
    17 int ccnt;
    18 int rock[MAXN];
    19 int a[MAXN],be[MAXN];
    20 void pre(int zone){
    21     memset(rock,0,sizeof(rock));
    22     int maxn=0,loc=0;
    23     for(register int i=(zone-1)*len+1;i<=n;i++){
    24         rock[a[i]]++;
    25         if(rock[a[i]]>maxn||(rock[a[i]]==maxn&&val[a[i]]<val[loc])){
    26             loc=a[i];
    27             maxn=rock[a[i]];
    28         }
    29         cnt[zone][be[i]]=loc;
    30     }
    31 }
    32 int query(int from,int to,int x){
    33     int temp=upper_bound(v[x].begin(),v[x].end(),to)-lower_bound(v[x].begin(),v[x].end(),from);
    34     return temp;
    35 }
    36 int query(int from,int to){
    37     int ans=cnt[be[from]+1][be[to]-1];
    38     int maxn=query(from,to,ans);
    39     for(register int i=from;i<=min(to,be[from]*len);i++){
    40         int temp=query(from,to,a[i]);
    41         if(temp>maxn||(temp==maxn&&val[a[i]]<val[ans])){
    42             ans=a[i];
    43             maxn=temp;
    44         }
    45     }
    46     if(be[from]!=be[to]){
    47         for(register int i=(be[to]-1)*len+1;i<=to;i++){
    48             int temp=query(from,to,a[i]);
    49             if(temp>maxn||(temp==maxn&&val[a[i]]<val[ans])){
    50                 ans=a[i];
    51                 maxn=temp;
    52             }
    53         }
    54     }
    55     return ans;
    56 }
    57 int main(){
    58     //freopen("txt.txt","r",stdin);
    59     scanf("%d%d",&n,&mm);
    60     len=sqrt(n);
    61     int ans=0;
    62     for(register int i=1;i<=n;i++) be[i]=(i-1)/len+1;
    63     for(register int i=1;i<=n;i++){
    64         scanf("%d",&a[i]);
    65         if(!m[a[i]]){
    66             m[a[i]]=++ccnt;
    67             val[ccnt]=a[i];//注意这里的离散化并不是需要排序,而只是给每一个数一个编号 
    68         }
    69         a[i]=m[a[i]];//给数列的每个数一个编号 
    70         v[a[i]].push_back(i);//该编号的数的位置加入一个i 
    71     }
    72     for(register int i=1;i<=be[n];i++) pre(i);//对每个块进行pre操作 
    73     for(register int i=1;i<=mm;i++){
    74         int aa,bb;
    75         scanf("%d%d",&aa,&bb);
    76         aa=(aa+ans-1)%n+1;bb=(bb+ans-1)%n+1;
    77         if(aa>bb)swap(aa,bb);
    78         ans=val[query(aa,bb)];
    79         printf("%d
    ",ans);
    80     }
    81     return 0;
    82 }
    83 /*
    84 20 3
    85 1 0 25 14 34 1 25 25 48 8 4 4 9 8 2 2 3 3 3 10
    86 1 20
    87 10 16
    88 4 8
    89 */
  • 相关阅读:
    Windows使用SCHTASKS 命令执行定时任务
    window10设置定时任务
    uiautomator2+python自动化测试1-环境准备
    uiautomator2+python自动化测试2-查看app页面元素利器weditor
    APPIUM 自带的webdriveragent
    使用 mitmproxy + python 做拦截代理
    mitmproxy 实战
    深入学习mitmproxy
    将博客搬至CSDN
    CS231N Assignment5 图像分类练习
  • 原文地址:https://www.cnblogs.com/ibilllee/p/8735248.html
Copyright © 2020-2023  润新知