分块算法 解决区间问题

块的操作主要有:

block是块的大小

t是块的数量

st是每个块的开始的下标

ed是每个块的结束的下标

pos是每个元素对应块的下标

sum是对应块的元素的和

add是增量标记 用于区间修改+区间查询

 核心代码如下:

const int MAX=10010;

int n;
int a[MAX];
int st[MAX],ed[MAX];
int pos[MAX];
int sum[MAX];
int add[MAX];
//区间修改
void change(int l,int r,int d){
    int p=pos[l];
    int q=pos[r];
    if(p==q){ //情况1 区间位于一个块里面
        for(int i=l;i<=r;i++){
            a[i]+=d;
        }
        sum[p]+=d*(r-l+1);
    }
    else { //区间跨多个块
        for(int i=p+1;i<=q-1;i++){
            add[i]+=d; //完整的块里面的数据直接加上d
        }
        for(int i=l;i<=ed[p];i++){ //整块前面的散块
            a[i]+=d;
        }
        sum[p]+=d*(ed[p]-l+1);
        for(int i=st[q];i<=r;i++){ //整块后面的散块
            a[i]+=d;
        }
        sum[q]+=d*(r-st[q]+1);
    }
}

ll query(int l,int r){
    int p=pos[l];
    int q=pos[r];
    ll ans=0;
    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+=sum[i]+add[i]*(ed[i]-st[i]+1); //先处理整块
        }
        for(int i=l;i<=ed[p];i++){ //整合散块
            ans+=a[i];
        }
        ans+=add[p]*(ed[p]-l+1);
        for(int i=st[q];i<=r;i++){
            ans+=a[i];
        }
        ans+=add[q]*(r-st[q]+1);
    }
    return ans;
}

int main(){
    
    
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    //start
    int block=sqrt(n);
    int t=n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1; //pos是第i个元素所在的块
    }
    
    
    for(int i=1;i<=t;i++){
        for(int j=st[i];j<=ed[i];j++){
            sum[i]+=a[j];  //sum是第i块的区间和
        }
    }
    //end
    
    int opt,l,r,c;
    for(int i=1;i<=n;i++){
        cin>>opt>>l>>r>>c;
        if(opt==1){
            cout<<query(r,r)<<endl;
        }
        else {
            change(l,r,c);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭晋龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值