分块————优雅的暴力

我认为,分块是一种比较巧妙,简洁易懂的算法,简称暴力

它主要的思想是将一串数列拆成sqrt(n)块,那么显然可以得出共有sqrt(n)个

将一个块可以看成一个整体,所包含的数可以同时变化,这样时间复杂度就会大大的减小

那怎样变化呢?        举个栗子

3    3    6    3    7    5    5    4    7    3    4    

共有十一个数(如上)

那么就可以分成4分(最后会有不足sqrt(n)的,也算一块)

3    3    6————1

3    7    5————2

5    4    7————3

3    4      ————4

如果要将2—-11每一个数加m,那么我们可以先得出2属于第一块,11属于第四块

它们中间的第三块和第二块均加m,可以用数组储存每一块整体需要加的数,这里用add[ ],

所以,add[2]+=m,add[3]+=m;如果后面要用的话,可以直接调用,这样就节约了很多时间

但是还是有些边边角角的数,那这个有一个巧妙的方法,暴力,是的,就是暴力,没有任何花样的暴力

————————————————————————————————————————————————

好的,分块基本思想讲完了,那该怎么建立块呢  

void make_block(){
    T=sqrt(number);
    for(int i=1;i<=T;i++)L[i]=(i-1)*sqrt(number)+1,R[i]=i*sqrt(number);
    if(R[T]<number)T++,R[T]=number,L[T]=R[T-1]+1;
    for(int i=1;i<=T;i++){
        for(int j=L[i];j<=R[i];j++)
           pos[j]=i,sum[i]+=num[j];
    }
}

pos存的是每一个数所在块,sum是一个块中数的和

那经过修改以后,对于第i个块,它的和为:    sum[i]+add[i]

由此,求某一区间数的和也更加方便

 

现有一道模板题:洛谷p3372【模板】线段树1  

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

 

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

 

输出格式:

 

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1: 
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1: 
11
8
20

很显然,上述两个操作基本思路已经讲述过了
下面是题解:
//分块 
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1000010;
ll number,num[N],pos[N],add[N],T,L[N],R[N],need,sum[N];

long long read(){
    long long s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

void make_block(){
    T=sqrt(number);
    for(int i=1;i<=T;i++)L[i]=(i-1)*sqrt(number)+1,R[i]=i*sqrt(number);
    if(R[T]<number)T++,R[T]=number,L[T]=R[T-1]+1;
    for(int i=1;i<=T;i++){
        for(int j=L[i];j<=R[i];j++)
           pos[j]=i,sum[i]+=num[j];
    }
}

void change(ll x,ll y,ll d){
    ll p=pos[x],q=pos[y];
    if(p==q){
        for(int i=x;i<=y;i++)num[i]+=d;
        sum[q]+=(y-x+1)*d;
    }
    else{
        for(int i=p+1;i<=q-1;i++)add[i]+=d;
        for(int i=x;i<=R[p];i++)num[i]+=d;
        sum[p]+=d*(R[p]-x+1);
        for(int i=L[q];i<=y;i++)num[i]+=d;
        sum[q]+=d*(y-L[q]+1);
    }
}

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

int main(){
    number=read();need=read();
    for(int i=1;i<=number;i++)num[i]=read();
    make_block();
    for(int i=1;i<=need;i++){
        ll pd=read(),x=read(),y=read(),d;
        if(pd==1){
            d=read();change(x,y,d);
        }
        else {
            cout<<ask(x,y)<<endl;
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/GMSD/p/11217964.html