分块入门总结

 前言

        这一切都是基于「分块」数列分块入门1 – 9,也就是对其的总结。


链接

        #6277. 数列分块入门 1                                         数列分块入门1 LibreOj 6277_Carry_hkr的博客-CSDN博客

        #6278. 数列分块入门 2                                         数列分块入门2 LibreOj 6278_Carry_hkr的博客-CSDN博客

        #6279. 数列分块入门 3                                         数列分块入门3 LibreOj 6279_Carry_hkr的博客-CSDN博客

        #6280. 数列分块入门 4                                         数列分块入门4 LibreOj 6280_Carry_hkr的博客-CSDN博客

        #6281. 数列分块入门 5                                         数列分块入门5 LibreOj 6281_Carry_hkr的博客-CSDN博客

        #6282. 数列分块入门 6                                         数列分块入门6 LibreOj 6282_Carry_hkr的博客-CSDN博客

        #6283. 数列分块入门 7                                         数列分块入门7 LibreOj 6283_Carry_hkr的博客-CSDN博客

        #6284. 数列分块入门 8                                         数列分块入门8 LibreOj 6284_Carry_hkr的博客-CSDN博客

        #6285. 数列分块入门 9                                         数列分块入门9 LibreOj 6285_Carry_hkr的博客-CSDN博客

总结

        这一路做下来,我觉得分块一般分成三个部分:1)预处理;2)操作;3)询问。有些时候的询问和操作是放在一起的,比如入门8的查询之后的赋值

        预处理:预处理一般处理一些block(块的长度),num(块的个数),l[i](块i的最左边的位置),r[i](块i的最右边位置),还有一个belong[i](表示第i个位置是属于哪一块的),常规的是这些,还有具体情况具体分析的,比如初始化flag标志数组为-1(如入门8)等。有些题解是没有写l,r数组的,因为可以直接表示出来,我觉得那样代码虽然短,但是不美观,有时候不太好理解,这样预处理之后可以一劳永逸了。

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;
    r[num]=n;
    for (int i=1;i<=n;i++)
        belong[i]=(i-1)/block+1;
    for (int i=1;i<=num;i++)
        for (int j=l[i];j<=r[i];j++)
            Max[i]=max(Max[i],a[j]);
    //这里拿区间最大值来举例
}

        操作:操作的话大部分是以区间操作进行,除了一些特殊的,比如入门6的单点插入,单点查询,区间操作分块就起大作用了。块,分为两种:完整块,我们一般用lazy等懒惰处理,需要用的时候再去进行更新等操作;不完整块,我们在类似lazy的基础上进行,有时候需要先全部更新一下,例如入门8的flag和reset,然后再暴力修改。还有一个重要的地方,如果对于区间比较小,也就是说belong[l]=belong[r],不完整块只有一块,不需要从右边界再处理一次不完整块。

inline void update(int x,int y,int c)
{
    if (belong[x]==belong[y])
    {
        for (int i=x;i<=y;i++) a[i]+=c;
        return;
    }
    for (int i=x;i<=r[belong[x]];i++) a[i]+=c;
    for (int i=belong[x]+1;i<belong[y];i++) lazy[i]+=c;
    for (int i=l[belong[y]];i<=y;i++) a[i]+=c;
    //这个用区间加c来举例
}

        查询:查询其实也跟操作一样,对于不完整块我们暴力查询,对于完整块我们就用类似于lazy操作得到答案,比如入门9的maxn[i][j]能直接得到第i块到第j块的最小众数。我们也需要注意一下左右区间边界是否是属于同一块的情况下,避免重复。

        其他:这些事总结起来的特殊情况。

                1)依情况而定,是否需要取余

                2)是否需要开long long

                3)对于一个区间排序二分,赋值的时候需要整体将整块赋值给排序数组,然后排序

                4)有些题目block不易太小,可以手动设置block=200等,不然会t


附上几张图

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值