分块,是一种优雅的暴力,它通过对数列分段,完成对数列一些区间操作和区间查询的操作,是一种根号算法。
分块的功能:
1.区间加;2.区间减;3.。。。。。。
4.查询区间和 3.查询任意区间内有多少个数大于等于k(注意,这个功能是我们使用分块而不选择线段树的重要依据)
在我的分块中,block表示原数组被分块后每块有多少个元素(不算最后一个);
l[i]表示第i块的最左元素在原数组的位置;r[i]则表示最右面的;
belong[i]表示原数组的第i个位置的元素属于第几个块;
tot则表示一共有多少个块;
一.建立块:
正常情况下:block=sqrt(n);
块的数量是n/block; 但是n不一定是一个完全平方数,我们需要把最后几个无法凑足block个元素的再单独分一个块。
tot=n/block; if(n%block) tot++;
然后l[i]=(i-1)*block+1; r[i]=i*block; r[tot]=n;
for(int i=1;i<=tot;i++){
L[i]=(i-1)*block+1; R[i]=i*block; } R[tot]=n;
belong[i]=(i-1)/block+1;
for(int i=1;i<=n;i++) belong[i]=(i-1)/block;
这项工作因题目不同而不同,如【教主的魔法】一题,就要对每个块的元素进行排序。
因为排序会对原始数列作出改变,所以在本题中,应当先把数列复制一遍再进行分块
二.修改:
在修改操作中,如果是整块,就不维护每个的具体信息,而是在这个块的lazy上进行操作;而对于并不覆盖整个区间的操作的剩余部分,我们使用暴力进行修改;
特别的,我们需要特判belong[x]==belong[r];
void change(int x,int y,int k) { if(belong[x]==belong[y]){ for(register int i=x;i<=y;i++){ a[i]+=k; } for(register int i=l[belong[x]];i<=r[belong[x]];i++){ b[i]=a[i]; } sort(b+l[belong[x]],b+r[belong[x]]+1); return ; } for(register int i=x;i<=r[belong[x]];i++){ a[i]+=k; } for(register int i=l[belong[x]];i<=r[belong[x]];i++){ b[i]=a[i]; } sort(b+l[belong[x]],b+r[belong[x]]+1); for(register int i=l[belong[y]];i<=y;i++){ a[i]+=k; } for(register int i=l[belong[y]];i<=r[belong[y]];i++){ b[i]=a[i]; } sort(b+l[belong[y]],b+r[belong[y]]+1); for(register int i=belong[x]+1;i<=belong[y]-1;i++){ lazy[i]+=k; } }
三.查询:
和修改的思想一样:能块上修改就修改,否则就暴力修改;
特别的,我们需要特判belong[x]==belong[r];
int query(int x,int y,int goal) { int ans=0; if(belong[x]==belong[y]){ for(register int i=x;i<=y;i++){ if(a[i]+lazy[belong[x]]>=goal) ++ans; } return ans; } for(register int i=x;i<=r[belong[x]];i++){ if(a[i]+lazy[belong[x]]>=goal) ++ans; } for(register int i=l[belong[y]];i<=y;i++){ if(a[i]+lazy[belong[y]]>=goal) ++ans; } for(register int i=belong[x]+1;i<=belong[y]-1;i++){ int L=l[i],R=r[i],mid; while(L=goal){ R=mid; } else{ L=mid+1; } } if(L==r[i]){ ans+=((b[L]+lazy[i])>=goal); continue; } ans+=(r[i]-L+1); } return ans; }
分块的思想并不难,难就难在对于代码的调试和细节;
尤其是每个数组的含义:每个数组的下标表示的是原数组的下标还是分块后块的下标;这我在下面的代码中已经注明;
接下来用本人A掉的第一道分块题来结束这篇文章;(教主的魔法)
#include#pragma GCC optimize(2) using namespace std; int a[1000010],b[1000010],belong[1000010]; //原来的 int lazy[1000010],l[1000010],r[1000010]; //块上的 int block,tot,n,m; void build() { block=sqrt(n); tot=n/block; if(n%block){ ++tot; } for(register int i=1;i<=n;i++){ b[i]=a[i]; belong[i]=(i-1)/block+1; } for(register int i=1;i<=tot;i++){ l[i]=(i-1)*block+1; r[i]=i*block; } r[tot]=n; for(register int i=1;i<=tot;i++){ sort(b+l[i],b+r[i]+1); } return; } void change(int x,int y,int k) { if(belong[x]==belong[y]){ for(register int i=x;i<=y;i++){ a[i]+=k; } for(register int i=l[belong[x]];i<=r[belong[x]];i++){ b[i]=a[i]; } sort(b+l[belong[x]],b+r[belong[x]]+1); return ; } for(register int i=x;i<=r[belong[x]];i++){ a[i]+=k; } for(register int i=l[belong[x]];i<=r[belong[x]];i++){ b[i]=a[i]; } sort(b+l[belong[x]],b+r[belong[x]]+1); for(register int i=l[belong[y]];i<=y;i++){ a[i]+=k; } for(register int i=l[belong[y]];i<=r[belong[y]];i++){ b[i]=a[i]; } sort(b+l[belong[y]],b+r[belong[y]]+1); for(register int i=belong[x]+1;i<=belong[y]-1;i++){ lazy[i]+=k; } } int query(int x,int y,int goal) { int ans=0; if(belong[x]==belong[y]){ for(register int i=x;i<=y;i++){ if(a[i]+lazy[belong[x]]>=goal) ++ans; } return ans; } for(register int i=x;i<=r[belong[x]];i++){ if(a[i]+lazy[belong[x]]>=goal) ++ans; } for(register int i=l[belong[y]];i<=y;i++){ if(a[i]+lazy[belong[y]]>=goal) ++ans; } for(register int i=belong[x]+1;i<=belong[y]-1;i++){ int L=l[i],R=r[i],mid; while(L =goal){ R=mid; } else{ L=mid+1; } } if(L==r[i]){ ans+=((b[L]+lazy[i])>=goal); continue; } ans+=(r[i]-L+1); } return ans; } int main() { cin>>n>>m; for(register int i=1;i<=n;i++) scanf("%d",&a[i]); build(); for(register int i=1;i<=m;i++){ char type; int x,y,k; cin>>type>>x>>y>>k; if(type=='M'){ change(x,y,k); } else{ cout<