分塊
- 時間複雜度:n * (根號n)
- 原理:把一個序列分成一塊塊的,每一塊有三個元素。一個是這個塊表示的序列左邊界left,右邊界right,攜帶的信息。(攜帶的信息可以爲區間最大值、區間和等等)
- 分塊三部曲
- 建塊(build)
- 更新(update)
- 查詢 (ask)
- 分塊與其它算法的比較
- 速度上:樹狀數組 < 線段樹 < 分塊
- 使用上:分塊通用性很強,樹狀數組還行,線段樹較單一
- 承受數據上:分塊極限20w,線段樹基本可以過50w,樹狀數組還可以比50w再大一些
建塊
- sizeBlock : 每一塊的大小
- numBlock : 塊的數量
- belong[i] : 表示i屬於哪個塊
- block結構體 : 即表示“塊”
- .l : 此塊表示的左邊界
- .r : 此塊表示的右邊界
- .val : 此塊表示的信息(下面案例以“區間和”爲例)
void build()
{
sizeBlock=(int)sqrt(n);
numBlock=n/sizeBlock;
if(n%sizeBlock) numBlock++;
for(int i=1;i<=numBlock;i++)
block[i].l=(i-1)*sizeBlock+1,block[i].r=i*sizeBlock;
block[numBlock].r=n;
for(int i=1;i<=n;i++) belong[i]=(i-1)/sizeBlock+1;
for(int i=1;i<=numBlock;i++)
for(int j=block[i].l;j<=block[i].r;j++)
block[i].val+=a[j];
}
更新
單點更新(以“區間和”爲例)
void update(int pos,int add) //函數功能:pos位置增加add
{
a[pos]+=add; //直接加
block[belong[pos]].val+=add; //pos這個元素所在塊的區間和更新
}
區間更新(以“區間和爲例”)
void update(int ll,int rr,int add)
{
if(belong[ll]==belong[rr])
{
for(int i=ll;i<=rr;i++)
a[i]+=add;
return;
}
for(int i=ll;i<=block[belong[ll]].r;i++) a[i]+=add;
for(int i=block[belong[rr]].l;i<=rr;i++) a[i]+=add;
for(int i=belong[ll]+1;i<belong[rr];i++) block[i].tag+=add;
}
查詢
單點查詢
int ask(int pos) //函數功能:返回pos位置的元素
{
return a[pos]
}
區間查詢(以“區間和”爲例)
int ask(int ll,int rr)
{
int ans=0;
if(belong[ll]==belong[rr])
{
for(int i=ll;i<=rr;i++)
ans+=a[i];
return ans;
}
for(int i=ll;i<=block[belong[ll]].r;i++) ans+=a[i];
for(int i=block[belong[rr]].l;i<=rr;i++) ans+=a[i];
for(int i=belong[ll]+1;i<belong[rr];i++) ans+=block[i].val;
return ans;
}
總結
- 注意區間更新與區間查詢查詢都是一個套路。先特判在同一塊的情況,然後去處理左邊多的和右邊多的,最後處理中間的塊
- 有些分塊題不一定按照模版寫。但是分塊的思想是亙古不變的
補充
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
struct Block{long long l,r,tag,val,len;}block[200005];
long long n,q,sizeBlock,numBlock;
long long a[200005],belong[200005];
void build()
{
sizeBlock=(long long)sqrt(n);
numBlock=n/sizeBlock;
if(n%sizeBlock) numBlock++;
for(long long i=1;i<=numBlock;i++)
block[i].l=(i-1)*sizeBlock+1,block[i].r=i*sizeBlock;
block[numBlock].r=n;
for(long long i=1;i<=n;i++) belong[i]=(i-1)/sizeBlock+1;
for(long long i=1;i<=numBlock;i++)
for(long long j=block[i].l;j<=block[i].r;j++)
block[i].val+=a[j];
for(long long i=1;i<=numBlock;i++)
block[i].len=block[i].r-block[i].l+1;
}
void update(long long ll,long long rr,long long add)
{
if(belong[ll]==belong[rr])
{
for(long long i=ll;i<=rr;i++) a[i]+=add;
block[belong[ll]].val+=(add*(rr-ll+1));
return;
}
for(long long i=ll;i<=block[belong[ll]].r;i++) a[i]+=add,block[belong[i]].val+=add;
for(long long i=block[belong[rr]].l;i<=rr;i++) a[i]+=add,block[belong[i]].val+=add;
for(long long i=belong[ll]+1;i<belong[rr];i++) block[i].tag+=add;
for(long long i=belong[ll]+1;i<belong[rr];i++) block[i].val+=(block[i].len*add);
}
long long ask(long long ll,long long rr)
{
long long ans=0;
if(belong[ll]==belong[rr])
{
for(long long i=ll;i<=rr;i++) ans+=(a[i]+block[belong[ll]].tag);
return ans;
}
for(long long i=ll;i<=block[belong[ll]].r;i++) ans+=(a[i]+block[belong[i]].tag);
for(long long i=block[belong[rr]].l;i<=rr;i++) ans+=(a[i]+block[belong[i]].tag);
for(long long i=belong[ll]+1;i<belong[rr];i++) ans+=block[i].val;
return ans;
}
int main()
{
cin>>n;
for(long long i=1;i<=n;i++) scanf("%lld",&a[i]);
build();
cin>>q;
for(long long i=1;i<=q;i++)
{
long long op,x,y,z; scanf("%lld",&op);
if(op==1) scanf("%lld%lld%lld",&x,&y,&z),update(x,y,z);
else if(op==2) scanf("%lld%lld",&x,&y),printf("%lld\n",ask(x,y));
}
return 0;
}