分块学习笔记 (一)

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;
  }
}

查询的话,就和上面的类似,读者可以先想一想,然后自己写一下。
未完待续…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值