1.什么是分块
给定一个数组,长度为n,把他分成m个小块,然后遇到什么区间更新的操作的时候,就可以大大的减少复杂度
2.怎么分块
通常我们把长度为n的数组分为ceil(n/sqrt(n))块,然后用l[maxn],r[maxn],数组来记录每一块的左边界和右边界。用数组belong[x]来记录x是属于哪一个块的。
3.分块初始化
void build(int n)
{
int B=sqrt(n)//每一个块的长度
int m=ceil(n/B);//块的个数
for(int i=1;i<=m;i++)
{
l[i]=(i-1)*B+1;
r[i]=i*B;
//确立左右边界
}
r[m]=n;
for(int i=1;i<=m;i++)
{
for(int j=l[i];j<=r[i];j++)
{
belong[j]=i;
}
//给每一个块打上标记
}
}
怎样用分块进行区间的更新
在用线段树进行区间更新操作的时候(如区间加减等),我们采用的是lazy思想,如果我们的在查询的时候,如果当前区间被包含在查询区间里面,我们就可以在这里打上一个标记,然后就不更新当前区间的子区间了,等查询的时候我们如果遇到这个区间,就直接用这个区间的长度和lazy的标记进行运算就可以得到该区间的权值,然后这个时候我们就可以用push_down函数进行向下更新,这样的话将大大的节约时间。为什么要先说线段树呢,因为分块的区间更新也可以用lazy操作,而且分块也和线段树有点像。然后给区间打标记后的操作要注意的是要完整区间和非完整区间的更新与查询,具体细节见如下代码
在这里我们以将某个区间的数全部替换成x为例子
void push_down(int x)
{
if(lazy[x]!=-1)
{
for(int i=l[x];i<=r[x];i++)
{
a[i]=x;
}
lazy[x]=-1;
}
}
void update(int x,int y,int v)
{
if(belong[x]==belong[y])
{
if(lazy[x]!=-1)
{
push_down(x);//将之前的标记打下来
}
for(int i=x;i<=y;i++)
{
a[i]=v;//更新
}
}
else
{
//当x和y不在同一块的时候
int b_st=belong[x]//确定x所在块
for(int i=x;i<=r[b_st];i++)
a[i]=v;
b_st++;
for(;b_st<belong[y];b_st++)
{
if(lazy[b_st]!=-1)
{
lazy[b_st]+=v;
}
else
{
lazy[b_st]=v;
}
}
if(lazy[b_st]!=-1)
{
push_down(b_st);
}
for(int i=l[b_st];i<=y;i++)
a[i]=v;
}
}
查询的话,就和上面的类似,读者可以先想一想,然后自己写一下。
未完待续…