数据结构:分块初学

分块

对于分块的解释…看名字就知道了嘛,就是把问题分成一块一块的来进行处理(听起来是不是很像分治,名字也挺像)。这个算法实际上和树状数组以及线段树相似,都可以进行在一段区间上的操作,但相较于这两者而言,分块的时间复杂度要高一些,但代码要简洁一些,更方便实现和查错!!!

具体操作

首先就是对于分块的长度的约束了,那么对于分块的长度,我们最好将它分成 n \sqrt{n} n 段长度为 n \sqrt{n} n 的块,这样就能使代码的时间复杂度最低了!

那么我们就来一道例题来康康这个算法的具体过程吧!

例题

https://loj.ac/p/6280

这道题就是对一个数组进行区间修改和区间求和。

对于每段区间的长度,可以得到 l e n = n len=\sqrt{n} len=n ,那么我们易得每个点所属于的块的编号即为 ( i − 1 ) ∗ l e n + 1 (i-1)*len+1 (i1)len+1

int get(i)
{
	return (i-1)*len+1;
}

接着是修改的操作。
对于每一个区间 [ l , r ] [l,r] [l,r]上的修改,我们可以分成两种情况,一种是l和r处于同一个块中,第二种是l和r处于不同的块中。
我们可以用两个数组来维护答案,sum[]维护当前块上的值的累加和,add[]维护当前块中的增加值。接下来我们就可以来进行修改。

一.对于l和r处于同一个块中。

在这里插入图片描述

因为每一块的长度 l e n ≤ n len\leq\sqrt{n} lenn ,所以即使是最坏的情况时间复杂度也只是 O ( n ) O(\sqrt{n}) O(n ),因此我们就可以暴力地从l到r一个一个进行修改。

	if(get(l)==get(r))
	{
		for(int i=l;i<=r;i++)
		{
			a[i]+=d;//d为增加的值
			sum[get(i)]+=d;
		}
	}

二.对于l和r处于不同的块中
在这里插入图片描述

首先同第一种情况相同,我们可以先暴力修改出l和r所在的块(即用红色斜线标出的区域),然后对于剩下的区域(即用红色叉叉标出的区域),全部都是整块,那么我们可以整体进行修改。

	if(get(l)!=get(r))
	{
		int i=l,j=r;
		while(get(i)==get(l)) a[i]+=d,sum[i]+=d,i++;
		while(get(j)==get(r)) a[j]+=d,sum[j]+=d,j--;
		for(int k=get(i);k<=get(j);k++) sum[k]+=len*d,add[k]+=d;
	}

那么整合一下,我们对于修改的操作便是:

	void change(int l,int r,int d)
	{
		if(get(l)==get(r))
		{
			for(int i=l;i<=r;i++)
			{
				a[i]+=d;
				sum[get(i)]+=d;
			}
		}
		else
		{
			int i=l,j=r;
			while(get(i)==get(l)) a[i]+=d,sum[i]+=d,i++;
			while(get(j)==get(r)) a[j]+=d,sum[j]+=d,j--;
			for(int k=get(i);k<=get(j);k++) sum[k]+=len*d,add[k]+=d;
		}
	}

接下来就是查询的操作。
实际上就和修改的步骤相似,同样分成两种情况,然后进行查找。
相信在座的都是大佬我就不详细解释了,直接上代码。真的不是我懒

	int work(int l,int r,int d)
	{
		int ans=0;
		if(get(l)==get(r))
		{
			for(int i=l;i<=r;i++) ans+=a[i]+add[get(i)],ans%=d;
		}
		else
		{
			int i=l,j=r;
			while(get(i)==get(l)) ans+=a[i]+add[get(i)],ans%=d,i++;
			while(get(j)==get(r)) ans+=a[j]+add[get(j)],ans%=d,j--;
			for(int k=get(i);k<=get(j);k++) ans+=sum[k],ans%=d;
			return ans%d;
		}
	}

代码到了这里就结束了,接下来奉上几份例题!!!
分块九题
数列分块入门1
数列分块入门2
数列分块入门3
数列分块入门4
数列分块入门5
数列分块入门6
数列分块入门7
数列分块入门8
数列分块入门9

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值