【分块】 A simple problem with Integer2

传送门

题意

给定一个长度为\(N\)的数列\(A\),以及\(M\)个指令,指令包含两种

  • \((C,l,r,d)\),把\(A[l],A[l+1],\dots ,A[r]\)都加上\(d\)

  • \((Q,l,r)\),询问区间\(l\sim r\)的区间和

数据范围

\(\begin{array}{l}1 \leq N, M \leq 10^{5} \\ |d| \leq 10000 \\ |A[i]| \leq 1000000000\end{array}\)

题解

分块做法,将长度为\(n\)的序列分作\(\sqrt{n}\)块,

  • 维护两个属性,当前完整块的和\(sum\),当前整个块共同加的数\(add\)

  • 查询和修改时,如果左右界都位于一个块直接进行暴力,

  • 否则先将左右的块内暴力处理,然后处理中间可能存在的完整块

每次修改和查询最多有\(2\)块是暴力做的,且暴力的长度\(\leq \sqrt{n}\),整个段的长度\(\leq \sqrt{n}\)
所以单次查询和修改的复杂度为\(O(\sqrt{n})\),总的时间复杂度为\(O((N + M)\times \sqrt{n})\)

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define ll long long 

const int N=1e5+10,M=350;
int a[N];
ll sum[M],add[M];
int len;
int n,m;
int get(int id){
    return id/len;
}
void modify(int l,int r,int d){
    if(get(l) == get(r)) rep(i,l,r) a[i]+=d,sum[get(i)]+=d;
    else{
        int i=l,j=r;
        while( get(i) == get(l) ) a[i]+=d,sum[get(i)]+=d,i++;
        while( get(j) == get(r) ) a[j]+=d,sum[get(j)]+=d,j--;
        rep(k,get(i),get(j)) sum[k]+=len*d,add[k]+=d;
    }
}
ll query(int l,int r){
    ll res=0;
    if(get(l)==get(r)){
        rep(i,l,r) res += a[i] + add[get(i)];
    }
    else{
        int i=l,j=r;
        while(get(i) == get(l)) res+=a[i]+add[get(i)],i++;
        while(get(j) == get(r)) res+=a[j]+add[get(j)],j--;
        rep(k,get(i),get(j)) res+=sum[k];
    }    
    return res;
}

int main(){
    cin>>n>>m;
    len = sqrt(n);

    rep(i,1,n) {
        cin>>a[i];
        sum[get(i)] += a[i];
    }

    char op[2];
    int l,r,d;
    while(m--){
        cin>>op>>l>>r;
        if(op[0] == 'C'){   
            cin >> d;
            modify(l,r,d);
        }
        else 
            cout<<query(l,r)<<endl;
    }
}
posted @ 2020-09-27 15:31  Hyx'  阅读(200)  评论(0编辑  收藏  举报