分块————优雅的暴力
我认为,分块是一种比较巧妙,简洁易懂的算法,简称暴力
它主要的思想是将一串数列拆成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的结果。
输入输出样例
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; }