专题 - 数列分块1

Get 新专题 —— 数列分块

来和大家分享一下

其实这是以前讲过的......笔者没认真听......现在复习算是明白了......

数列分块板子题 - LOJ 6277 数列分块入门1

挂上链接 - https://loj.ac/problem/6277

题面很简单 - 数列的区间加法,单点查值

正文部分

数列长度,操作数量最大可达50000,单点查值问题不大,可区间加法就太坑了,如果硬做,时间复杂度可达O(n^2),肯定超时......

众所周知,操作越宏观复杂度越低。不难发现,区间加法一个一个加太过微观,要设法往宏观拔。分块就是一种方法。

顾名思义,分块就是把数列分成若干块。怎么分呢?设块数为a,一块数量为b,总量为n。我们希望a与b相对平衡(原因一会就会明了)。根据a与b的数量关系,即a*b=n,为使其平衡,不妨令a,b都为sqrt(n)(根号n)。

举个栗子:

分完块后就可以开始操作了

先来理解一下分块。分块的精髓,即"大段维护,小段朴素"。大段即整段,也就是一个分块,小段即操作区间两端不满一块的部分。维护即整段操作,朴素即原始的一个数一个数的操作。

对于这题,区间加法就可以运用"大段维护,小段朴素"。将操作区间分为"大段","小段"。

"大段"操作,扫描每一个"大段",加到一个数中,将加一个一个加化为整体加,将微观化为宏观。

for(int i=p[l]+1; i<=p[r]-1; i++){
    atag[i]+=c; //大段统一加 
}

"小段"一般有两段,即左端和右端,分别一个数一个数的加

for(int i=l; i<=min(p[l]*sqrtN,r); i++)
    a[i]+=c; //左边的不满一块的部分进行"朴素" 
if(p[l]!=p[r])
    for(int i=(p[r]-1)*sqrtN+1; i<=r; i++)
       a[i]+=c; //右边 

因为大段数量不会超过sqrt(n),小段中的数的总数也不会超2*sqrt(n),所以区间加法的总复杂度从O(n^2)级别降到了O(n*sqrt(n))级别

单点查询中单点的值=其单独的值+所在区间的统一加的值

a[R]+atag[p[R]]

那么,分块1到这就基本结束啦,下面会附上完整代码(C++)和精心设计的注解方便理解

完整代码:

// LOJ 6277 - 数列分块入门 1 
// https://loj.ac/problem/6277
// 分块 

#include
#define M 50005
using namespace std;

int sqrtN; 
int a[M], atag[M], p[M];
//a[] -  数列 
//atag[] -  区间统一加值 
//p[] -  数列中对应下标的数所在的区间下标 

void add(int l, int r, int c){//l为区间左坐标,r为右坐标 ,c为加数 
    for(int i=l; i<=min(p[l]*sqrtN,r); i++){
        a[i]+=c; //左边的不满一块的部分进行"朴素" 
    }
    if(p[l]!=p[r]){
        for(int i=(p[r]-1)*sqrtN+1; i<=r; i++){
            a[i]+=c; //右边 
        }
    }
    for(int i=p[l]+1; i<=p[r]-1; i++){
        atag[i]+=c; //大段统一加 
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout); 
    
    int N;
    cin >> N;
    sqrtN=sqrt(N);
    
    for(int i=1; i<=N; i++){
        cin >> a[i];
        atag[i]=0;
        p[i]=(i-1)/sqrtN+1;
    }//预处理 
    
    for(int i=1; i<=N; i++){
        int F, L, R, C;
        cin >> F >> L >> R >> C;
        if(F==0) add(L, R, C); //区间加法 
        if(F==1) cout << a[R]+atag[p[R]] << endl;//单点查值 
    }
    
    return 0;
}

如有疑问欢迎在下方评论区提出

你可能感兴趣的:(c++)