分块——学习笔记
分块简介
分块被称为优雅的暴力,核心在于把一段长度为的区间分割成若干个长度为的区间,对于可以覆盖某个区间的操作,我们整体处理,不能覆盖整个区间,就直接单个暴力修改
尽管我不会证明,但是分块的查询复杂度在~之间,平均下来是,相对线段树和树状数组,它的时间复杂度的确是高一点,伴随这一点的是,它的空间复杂度和可拓展性是这三个东西里面最高的,分块能搞的线段树和树状数组不一定能搞
具体实现
我们搬出了经典的题目,洛谷上的几道线段树模板题
以这题为蓝本我们来讨论如何实现分块
1.怎么分
我们要把分为,显然这么做是为了均分时间复杂度
需要注意的是实际上我们并不能将它分为个,因为对于很多来说平方是开不尽的,我们会剩下一些数,那么我们就给它新开一个块
然后我们也需要统计一些信息,比如:某个数属于哪个块,每个块的左右边界,还有相关需要的信息(在这道题中是区间中数字的和)
2.怎么用
对于这道题,我们对于每个块,维护两个信息,代表了第块的总和,代表了这块每个数加了多少,对于数列中的每个数,维护它的原始值
分两种情况,如果当前修改的区间恰好可以覆盖这个块,我们直接修改和(这很容易,不用多解释);如果无法完整覆盖,那就直接循环,利用和可以很简单地求出它的和
(此代码引用自此题一位大佬远航之曲的题解)
#include<bits/stdc++.h>
using namespace std;
#define long long LL;
const int maxn = 100010;
int n,m,num,q,belong[maxn],block,l[maxn],r[maxn];
LL a[maxn],mark[maxn],d[maxn],x,com,y,z;
char s[10];
LL inline read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void build()//建块
{
block=sqrt(n);
num=n/block;if (n%block) num++;//没办法开尽根
for (int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;//标记这一个块的左右范围
for (int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;//某个数属于哪一个块
for (int i=1;i<=n;i++)
d[belong[i]]+=a[i];//维护块的和
}
void update(int L,int R,LL x)
{
if (belong[L]==belong[R])//属于同一块,直接暴力修改
{
for (int i=l[belong[L]];i<=r[belong[R]];i++)
a[i]+=mark[belong[L]];
d[belong[L]]+=mark[belong[L]]*block;
mark[belong[L]]=0;
for (int i=L;i<=R;i++)
a[i]+=x;
d[belong[L]]+=(R-L+1)*x;
return;
}
//完整的块整体加
for (int i=l[belong[L]];i<=r[belong[L]];i++)
a[i]+=mark[belong[L]];
d[belong[L]]+=mark[belong[L]]*block;
mark[belong[L]]=0;
for (int i=L;i<=r[belong[L]];i++)
a[i]+=x;
d[belong[L]]+=(r[belong[L]]-L+1)*x;
for (int i=l[belong[R]];i<=r[belong[R]];i++)
a[i]+=mark[belong[R]];
d[belong[R]]+=mark[belong[R]]*block;
mark[belong[R]]=0;
for (int i=l[belong[R]];i<=R;i++)
a[i]+=x;
d[belong[R]]+=(R-l[belong[R]]+1)*x;
for (int i=belong[L]+1;i<belong[R];i++)
mark[i]+=x;
}
void query(int L,int R)
{
LL ans=0;
if (belong[L]==belong[R])
{
for (int i=L;i<=R;i++)
ans+=a[i];
printf("%lld\n",ans+(R-L+1)*mark[belong[R]]);
return;
}
for (int i=L;i<=r[belong[L]];i++)
ans+=a[i]+mark[belong[L]];
for (int i=l[belong[R]];i<=R;i++)
ans+=a[i]+mark[belong[R]];
for (int i=belong[L]+1;i<belong[R];i++)
ans+=d[i]+mark[i]*block;
printf("%lld\n",ans);
}
main()
{
n=read(),q=read();
for (int i=1;i<=n;i++)
a[i]=read();
build();
for (int i=1;i<=q;i++)
{
com=read(),x=read(),y=read();
if (com==2)
query(x,y);
else
z=read(),update(x,y,z);
}
}
本文来自博客园,作者:羊扬羊,转载请注明原文链接:https://www.cnblogs.com/sheepcsy/p/16506267.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探