分块

闲的没事就学了学...

概况:

 

分块,顾名思义,就是把一段序列分成一小块一小块得来处理,维护。

 

我们把一段当成一个整体,只记录维护整体的有关信息,就是分块。

实现:

直接在代码里解释吧...

 

//a[N] 输入的数组
//pos[N] 元素所在的区域
//sum[N] 一个区域的元素总和
//add[N] 记录对这个区间的修改(有点像lazy_tag)
//L[N] 每个区间的左端点
//R[N] 每个区间的右端点

 

初始化操作:

cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int t=sqrt(n);//分块的大小
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1; 
        R[i]=i*t;//初始化边界大小
    }
    if(R[t]<n){//如果剩下了一些元素,把剩下的元素合并成一个块
        t++;
        L[t]=t*t+1;
        R[t]=n;
    } 
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;//每个元素所在块的编号
            sum[i]+=a[j];//每个块的元素总和
        }
    }

添加操作:

void modify(int l,int r,int k){
    int p=pos[l];
    int q=pos[r];
    if(p==q){//如果修改区间在一个块内
        for(int i=l;i<=r;i++) a[i]+=k;
        sum[p]+=(l-r+1)*k;//直接修改每个元素和总和
    }
    else{//不在一个块内
        for(int i=p+1;i<=q-1;i++) add[i]+=k;//用add[]记录在其中的块的变化
        for(int i=l;i<=R[p];i++) a[i]+=k;
        sum[p]+=(R[p]-l+1)*k;
        for(int i=L[q];i<=r;i++) a[i]+=k;
        sum[q]+=(r-L[q]+1)*k;//剩下的左边一点和右边一点操作同在一个区间的情况
    }
}

 

修改操作:

 

int ask(int l,int r){
    int ans=0;
    int p=pos[l];
    int q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++) ans+=a[i];
        ans+=add[p]*(r-l+1);//在一个区间直接找答案
    }
    else{
        for(int i=p+1;i<=q-1;i++) ans=ans+sum[i]+add[i]*(R[i]-L[i]+1);//统计整块区间答案
        for(int i=l;i<=R[p];i++) ans+=a[i];
        ans+=add[p]*(R[p]-l+1);
        for(int i=L[q];i<=r;i++) ans+=a[i];
        ans+=add[q]*(r-L[q]+1);//处理左边和右边的零碎区间
    }
    return ans;
}

 

可以发现,分块其实就是把大区间操作整体实现,小区间就直接朴素地进行操作。

总结为八字:

大段维护,局部朴素。

其实就是优雅的暴力。

模板Code:

#include<bits/stdc++.h>
using namespace std;
const int N=101;
int a[N],pos[N],sum[N],add[N];
int L[N],R[N];
int n,m;
int op;
int ll,rr,k;

void modify(int l,int r,int k){
    int p=pos[l];
    int q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++) a[i]+=k;
        sum[p]+=(l-r+1)*k;
    }
    else{
        for(int i=p+1;i<=q-1;i++) add[i]+=k;
        for(int i=l;i<=R[p];i++) a[i]+=k;
        sum[p]+=(R[p]-l+1)*k;
        for(int i=L[q];i<=r;i++) a[i]+=k;
        sum[q]+=(r-L[q]+1)*k;
    }
}

int ask(int l,int r){
    int ans=0;
    int p=pos[l];
    int q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++) ans+=a[i];
        ans+=add[p]*(r-l+1);
    }
    else{
        for(int i=p+1;i<=q-1;i++) ans=ans+sum[i]+add[i]*(R[i]-L[i]+1);
        for(int i=l;i<=R[p];i++) ans+=a[i];
        ans+=add[p]*(R[p]-l+1);
        for(int i=L[q];i<=r;i++) ans+=a[i];
        ans+=add[q]*(r-L[q]+1);
    }
    return ans;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int t=sqrt(n);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1; 
        R[i]=i*t;
    }
    if(R[t]<n){
        t++;
        L[t]=t*t+1;
        R[t]=n;
    } 
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
            sum[i]+=a[j];
        }
    }
    for(int i=1;i<=m;i++){
        cin>>op;
        if(op==1){
            cin>>ll>>rr>>k;
            modify(ll,rr,k);
        }
        if(op==2){
            cin>>ll>>rr;
            cout<<ask(ll,rr);
        }
    }
    return 0;
}

 

posted @ 2021-02-25 15:26  爆零王  阅读(76)  评论(0编辑  收藏  举报