分块——学习笔记

分块简介

分块被称为优雅的暴力,核心在于把一段长度为n的区间分割成若干个长度为n的区间,对于可以覆盖某个区间的操作,我们整体处理,不能覆盖整个区间,就直接单个暴力修改

尽管我不会证明,但是分块的查询复杂度在O(1)~O(n)之间,平均下来是O(n),相对线段树和树状数组,它的时间复杂度的确是高一点,伴随这一点的是,它的空间复杂度和可拓展性是这三个东西里面最高的,分块能搞的线段树和树状数组不一定能搞

具体实现

我们搬出了经典的题目,洛谷上的几道线段树模板题

以这题为蓝本我们来讨论如何实现分块

1.怎么分

我们要把n分为n,显然这么做是为了均分时间复杂度
需要注意的是实际上我们并不能将它分为n个,因为对于很多n来说平方是开不尽的,我们会剩下一些数,那么我们就给它新开一个块
然后我们也需要统计一些信息,比如:某个数属于哪个块,每个块的左右边界,还有相关需要的信息(在这道题中是区间中数字的和)

2.怎么用

对于这道题,我们对于每个块,维护两个信息,sum[i]代表了第i块的总和,add[i]代表了这块每个数加了多少,对于数列中的每个数,维护它的原始值a[i]

分两种情况,如果当前修改的区间恰好可以覆盖这个块,我们直接修改sum[i]add[i](这很容易,不用多解释);如果无法完整覆盖,那就直接循环,利用a[i]add[i]可以很简单地求出它的和

(此代码引用自此题一位大佬远航之曲的题解)

#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);
    }
}
posted @   羊扬羊  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示