学习笔记:分块

前言

非常粗略

概念

什么是分块算法?
很简单 就是暴力 把一段长度为 n 的序列 分成 n 块 块长为 n 然后进行一系列暴力乱搞
它的好处就是非常暴力 好!
先来看一道 板子

题目要求我们区间加一个数 区间查询一段和
这个东西怎么搞?
考虑分块!
首先呢 把原数列分为了 n 块 可能末尾还会多一块不足 n 长度的块
然后 我们需要预处理以下的值:

  • belx 查询每个数在哪个块
  • Lx 这个块的左端点
  • Rx 这个块的右端点
  • block 块长
  • tot 块数
    注意处理最后一块的边界和块数即可

易得每一块的左端点为 (i1)×block+1 右端点为 i×block
根据这样算一下即可

void build()
{
	block=sqrt(n);
	tot=n/block;
	if(n%block) tot++;
	for(int i=1;i<=tot;i++)
		L[i]=R[i-1]+1,R[i]=i*block;
	R[tot]=n;
	for(int i=1;i<=n;i++)
		bel[i]=(i-1)/block+1,sum[bel[i]]+=a[i];
}

预处理完毕 思考怎么修改/求中间一段
其实很简单

  • 对于中间的整段 借用线段树的思想 打个 tag 走路即可 不超过 O(n)
  • 对于左右的零散段 暴力改即可 不超过 O(n)

时间复杂度 O(n)
注意特判在一个块内的即可

void updata(int l,int r,ll x)
{
	int ls=bel[l],rs=bel[r];
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			a[i]+=x,sum[ls]+=x;
		return ;
	}
	for(int i=l;i<=R[ls];i++)
		a[i]+=x,sum[ls]+=x;
	for(int i=L[rs];i<=r;i++)
		a[i]+=x,sum[rs]+=x;
	for(int i=ls+1;i<=rs-1;i++)
		tag[i]+=x,sum[i]+=(R[i]-L[i]+1)*x;
	return ;
}

然后是查询
借用和修改一样的操作就行 时间复杂度 O(n)

ll query(int l,int r)
{
	int ls=bel[l],rs=bel[r];
	ll ans=0;
	if(ls==rs)
	{
		for(int i=l;i<=r;i++)
			ans+=a[i]+tag[ls];
		return ans;
	}
	for(int i=l;i<=R[ls];i++)
		ans+=a[i]+tag[ls];
	for(int i=L[rs];i<=r;i++)
		ans+=a[i]+tag[rs];
	for(int i=ls+1;i<=rs-1;i++)
		ans+=sum[i];
	return ans;
}

这样 我们优雅的暴力程序就出来了 时间复杂度 O(nn) 还是很不错的

posted @   g1ove  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示