# 分块
分块是一种将一些需要维护的数据划分为一些小块后,预处理每个小块的信息从而实现较暴力更优的复杂度的一种思想,优化后的复杂度一般是 的
接下来从最简单的求区间和来看看分块的实现方法
对于一个长度为 的序列 ,我们将这个序列分成连续的大小相同的 块
注意最后一个块可能并不完整,但这并没有多大影响
那么我们可以维护每个块的所有元素之和,这样我们在查询区间 的和时,就可以快速计算
如果 和 在一个块中,那么直接暴力求和即可,复杂度最坏为
如果 和 不在一个块中,那么我们要求的区间一定能划分成以 开头的不完整块 + 一些完整块 + 以 结尾的不完整块,对完整的块,直接加上这个块的元素之和即可,不完整的块直接暴力求和,最坏复杂度为
根据均值不等式 可知当 时,不等式有最小值,即取块长为 时,复杂度最优
如果要对这个块进行单点修改的话,完全可以暴力更新序列的值和对应块维护的该块的值
对于区间修改,我们可以类比查询操作,对于不完整的块,我们直接暴力修改,对于完整的块,我们再维护一个该块加过的值 ,类似线段树的懒标记,查询的时候每个元素的真实值是
#include<bits/stdc++.h> | |
#define int long long | |
#define in inline | |
#define ri register | |
#define _123hh2 0 | |
using namespace std; | |
in int read() | |
{ | |
int x=0,f=1;char ch=getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();} | |
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}return x*f; | |
} | |
in void write(int x) {if(x<0) {x=-x;putchar('-');}if(x>9) write(x/10);putchar(x%10+'0');} | |
const int maxn=1e5+5; | |
const int maxm=1e3+5; | |
int len;// 块长 | |
int awa[maxn]; | |
int id[maxn];// 每个位置对应的块的编号 | |
int tag[maxm];// 完整块的加值情况 | |
int sum[maxm];// 块内元素和 | |
void add(int l,int r,int val) | |
{ | |
int L=id[l],R=id[r]; | |
if(L==R) | |
{ | |
for(int i=l;i<=r;i++) awa[i]+=val,sum[L]+=val; | |
} | |
else | |
{ | |
for(int i=l;id[i]==L;i++) awa[i]+=val,sum[L]+=val; | |
for(int i=L+1;i<=R-1;i++) tag[i]+=val,sum[i]+=val*len; | |
for(int i=r;id[i]==R;i--) awa[i]+=val,sum[L]+=val; | |
} | |
} | |
void query(int l,int r) | |
{ | |
int L=id[l],R=id[r],ans=0; | |
if(L==R) | |
{ | |
for(int i=l;i<=r;i++) ans+=awa[i]+tag[L]; | |
} | |
else | |
{ | |
for(int i=l;id[i]==L;i++) ans+=awa[i]+tag[L]; | |
for(int i=L+1;i<=R-1;i++) ans+=sum[i]; | |
for(int i=r;id[i]==R;i--) ans+=awa[i]+tag[R]; | |
} | |
cout<<ans<<endl; | |
} | |
signed main() | |
{ | |
int n=read(),q=read(); | |
len=sqrt(n);// 块长为 len | |
for(int i=1;i<=n;i++) | |
{ | |
awa[i]=read(); | |
id[i]=(i-1)/len+1; | |
sum[id[i]]+=awa[i]; | |
} | |
while(q-->0) | |
{ | |
int op=read(),l=read(),r=read(); | |
if(op==1) | |
{ | |
int val=read(); | |
add(l,r,val); | |
} | |
else query(l,r); | |
} | |
return _123hh2; | |
} |
# 例题
P1975 [国家集训队] 排队
把序列分块后,对每个块进行排序,交换 和 位置的数相当于用 比较 区间内的数,不完整的块暴力查找,完整块二分查找即可
#include<bits/stdc++.h> | |
#define int long long | |
#define in inline | |
#define ri register | |
#define _123hh2 0 | |
using namespace std; | |
in int read() | |
{ | |
int x=0,f=1;char ch=getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();} | |
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}return x*f; | |
} | |
in void write(int x) {if(x<0) {x=-x;putchar('-');}if(x>9) write(x/10);putchar(x%10+'0');} | |
const int maxn=2e4+5; | |
int awa[maxn],ans,len,n; | |
vector<int> merge(int l,int r) | |
{ | |
vector<int> now; | |
now.push_back(awa[l]); | |
if(l==r) return now; | |
int mid=(l+r)/2; | |
vector<int> a=merge(l,mid); | |
vector<int> b=merge(mid+1,r); | |
vector<int> c; | |
int L=0,R=0; | |
while(L!=a.size()&&R!=b.size()) | |
{ | |
if(a[L]>b[R]) ans+=a.size()-L,c.push_back(b[R]),R++; | |
else c.push_back(a[L]),L++; | |
} | |
while(L!=a.size()) c.push_back(a[L]),L++; | |
while(R!=b.size()) c.push_back(b[R]),R++; | |
return c; | |
} | |
int id[maxn]; | |
struct A {int r,awa[201];}block[maxn]; | |
void deal(int l,int r) | |
{ | |
int L=id[l],R=id[r]; | |
if(L==R) | |
{ | |
for(int i=l+1;i<r;i++) | |
{ | |
if(awa[l]>awa[i]) ans--; | |
if(awa[l]<awa[i]) ans++; | |
} | |
for(int i=l;i<r;i++) | |
{ | |
if(awa[r]>awa[i]) ans++; | |
if(awa[r]<awa[i]) ans--; | |
} | |
swap(awa[l],awa[r]); | |
return; | |
} | |
for(int i=l+1;id[i]==L;i++) | |
{ | |
if(awa[l]>awa[i]) ans--; | |
if(awa[l]<awa[i]) ans++; | |
} | |
for(int i=r-1;id[i]==R;i--) | |
{ | |
if(awa[l]>awa[i]) ans--; | |
if(awa[l]<awa[i]) ans++; | |
} | |
for(int i=l;id[i]==L;i++) | |
{ | |
if(awa[r]>awa[i]) ans++; | |
if(awa[r]<awa[i]) ans--; | |
} | |
for(int i=r-1;id[i]==R;i--) | |
{ | |
if(awa[r]>awa[i]) ans++; | |
if(awa[r]<awa[i]) ans--; | |
} | |
for(int i=L+1;i<R;i++) | |
{ | |
int nl=1,nr=block[i].r,res=0; | |
while(nl<=nr) | |
{ | |
int mid=(nl+nr)/2; | |
if(block[i].awa[mid]<awa[l]) res=mid,nl=mid+1; | |
else nr=mid-1; | |
} | |
ans-=res; | |
nl=1,nr=block[i].r,res=block[i].r+1; | |
while(nl<=nr) | |
{ | |
int mid=(nl+nr)/2; | |
if(block[i].awa[mid]>awa[l]) res=mid,nr=mid-1; | |
else nl=mid+1; | |
} | |
ans+=block[i].r-res+1; | |
nl=1,nr=block[i].r,res=block[i].r+1; | |
while(nl<=nr) | |
{ | |
int mid=(nl+nr)/2; | |
if(block[i].awa[mid]>awa[r]) res=mid,nr=mid-1; | |
else nl=mid+1; | |
} | |
ans-=block[i].r-res+1; | |
nl=1,nr=block[i].r,res=0; | |
while(nl<=nr) | |
{ | |
int mid=(nl+nr)/2; | |
if(block[i].awa[mid]<awa[r]) res=mid,nl=mid+1; | |
else nr=mid-1; | |
} | |
ans+=res; | |
} | |
for(int i=1;i<=block[L].r;i++) | |
{ | |
if(block[L].awa[i]==awa[l]) | |
{ | |
block[L].awa[i]=awa[r]; | |
break; | |
} | |
} | |
for(int i=1;i<=block[R].r;i++) | |
{ | |
if(block[R].awa[i]==awa[r]) | |
{ | |
block[R].awa[i]=awa[l]; | |
break; | |
} | |
} | |
swap(awa[l],awa[r]); | |
sort(block[L].awa+1,block[L].awa+1+block[L].r); | |
sort(block[R].awa+1,block[R].awa+1+block[R].r); | |
} | |
signed main() | |
{ | |
n=read(); | |
len=sqrt(n); | |
for(int i=1;i<=n;i++) awa[i]=read(); | |
for(int i=1;i<=n;i++) | |
{ | |
id[i]=(i-1)/len+1; | |
block[id[i]].r++; | |
block[id[i]].awa[block[id[i]].r]=awa[i]; | |
} | |
for(int i=1;i<=(n-1)/len+1;i++) sort(block[i].awa+1,block[i].awa+1+block[i].r); | |
merge(1,n); | |
cout<<ans<<endl; | |
int q=read(); | |
while(q-->0) | |
{ | |
int l=read(),r=read(); | |
if(l>r) swap(l,r); | |
deal(l,r); | |
cout<<ans<<endl; | |
} | |
return _123hh2; | |
} |