分块
分块算法就是将一个序列分成若干块,分别进行操作来解决问题。总而言之就是“整块打包维护,碎片逐个枚举”。
基本操作
//block每一块大小 sz 分块数目 pos[i] 第i个数对应的是第几个块
//st[j]第j个块开始的位置 ed[j]第j个块结束的位置
block=sqrt(n);
sz=n/block;
if(n%block) sz++;
for(int i=1;i<=sz;++i) {
st[i]=(i-1)*block+1;
ed[i]=i*block;
}
ed[sz]=n;
for(int i=1;i<=n;i++) {
pos[i]=(i-1)/block+1;
}
以例题P3372来讲解分块的应用
问题:对一个序列可以进行两种操作 1:将某个区间加上k; 2:求出某个区间的和。
分块解决方案 :
(1)定义数组a[]: 存储原始数据 数组sum[]:表示每一个块的 数组add[]: 表示每一个块加上数
(2)对于[L,R]区间修改: p=pos[L],q=pos[R]。若q=p即修改区间在一个块内,那么直接遍历修改数组a[]和对sum[p]+=d*(R-L+1)。若p!=q,在修改区间完全包含的快上直接对其add进行修改。对不完全包含在区间内的块直接对其sum和a[]修改。
(3)对于区间[L,R]区间求和, p=pos[L],q=pos[R]。若p=q即求和区间在一个块内,直接遍历区间进行求和res=a[L]+...a[R],
在加上add[p]的值,即res+=add[p]*(R-L+1);反之,对完全包含在区间内的快遍历求其和,然后再按照不完全包含的情况逐个遍历求解。
void modify(int d,int L,int R) {
int p = pos[L], q = pos[R];
if(p==q) {
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;
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], q = pos[R];
ll res=0;
if(p==q){
for(int i=L;i<=R;i++) res+=a[i];
res+=add[p]*(R-L+1);
}
else{
for(int i=p+1;i<=q-1;i++) res+=sum[i]+add[i]*(ed[i]-st[i]+1);//整块
//部分块
for(int i=L;i<=ed[p];i++) res+=a[i];
res+=add[p]*(ed[p]-L+1);
for(int i=st[q];i<=R;i++) res+=a[i];
res+=add[q]*(R-st[q]+1);
}
return res;
}