分块学习小结

明天要开始讲二分、分块、倍增、差分等,今天先补一补!

基础知识

引用别人的一段话:一直觉得分块是一个很高端的东西…一直没敢碰,现在才知道分块就是一种稍微优美一些的暴力,所以没有学过分块的同学不要害怕啦…

分块,就是将一段信息(数据结构,序列)分成几块处理,当然最基础的是序列上的分块,再高层点的就是树上的分块,分块套分块,分块套数据结构以及数据结构套分块。 

顾名思义就是把要处理的东西进行分块,分成一块一块的,举个很简单的例子,对于一个数列 size(a{ })=5,我们可以把前2个分到一起,再两个分到一起,最后单下来一个,为什么要这样处理呢?这样处理的好处又是什么呢?

我们也可以这样思考,对于一个数列,当该数列的长度为n的时候,我们以根号n为一段,分出来的段的总数量不超过根号n。例如:1、2、3、4、5、6、7、8、9、10这10个数,以3(根号10=3)个数字为一段,可以分成第一段1、2、3,第二段4、5、6,第三段7、8、9,分出来的总段数不超过3(根号10),当然还有第四段残留块10;

(这里附上概念区间加减法:比如上面的数列就是一个区间:1.将区间每一个数加上或减去x;2.将区间每一个数乘上x。)

如果我们要进行区间的处理,比如加法减法等,对于待修改区间[ L , R ],可以把其中框起来的块(一块是根号n的大小)直接打上标记(用线段树的标记思想,打下标记),由于每一块的长度不大于根号n,所以对于两边没有框起来的部分,我们直接暴力地进行更新,这样操作次数是最多2倍根号n的,而中间的标记是O(1)处理的,这就是为什么该类算法是根号级别的原因。

那么每一个区间更新等价于对完整的块更新和剩余下来单个元素的更新。

分块解法

我们将这长度为n的序列每m个分成一块,那么就有n/m个块,以及会多出不超过m个单位的数。 
在区间修改时,先把这段区间内的每个块修改,用线段树的标记思想,打下标记,然后多出的那些不完整的部分直接暴力修改权值。 
时间复杂度是:修改块O(n/m)O(n/m)+修改散数O(m)O(m),总O(n/m+m)O(n/m+m)。 
运用一些数学知识(均值不等式),当m为n−−√n时,总复杂度最低。


分块模板:

block=sqrt(n); //每一个块的个数。
for(int i=1;i<=n;i++)
{
    bl[i]=(i-1)/block+1; //bl数组表示第i个位置属于的块是哪个
}
bl[0]=1;
bl[n+1]=bl[n]; //我会处理边界
for(int i=1;i<=n;i++)
{
    int x,y;
    scanf("%d%d",&x,&y);
    add(x,y); //区间修改
}
for(int i=1;i<=n-1;i++)
{
    printf("%d ",a[i]+lab[bl[i]]); //答案是本身的值加块上的标记的值
}
printf("%d\n",a[n]+lab[bl[n]]);//这是在交题时的格式问题,直接忽略就是了

区间修改模板:

void add(int a,int b,int c)
{
    bool bz1,bz2;//我比较弱,用bz1,bz2来记录x,y是否是处于各自块的边界
    if (bl[a-1]==bl[a]) bz1=false;
    else bz1=true;
    if (bl[b+1]==bl[b]) bz2=false;
    else bz2=true;
    if (bl[a]==bl[b]){
        if (bz1 && bz2) lab[bl[a]]+=c;//lab表示第i个块当前的修改标记
        else for(i=a;i<=b;i++) v[i]+=c;
    }
    else {
        if (bz1 && bz2) 
            fo(i,bl[a],bl[b]) lab[i]+=c;
        else if (bz1 && !bz2) {
            fo(int i=bl[a];i<=bl[b]-1;i++) lab[i]+=c;//整块打下标记
            fo(int i=(bl[b]-1)*block+1;i<=b;i++) v[i]+=c;//多余部分直接暴力修改
        }
        else if (!bz1 && bz2) {
            fo(int i=bl[a]+1;i<=bl[b];i++) lab[i]+=c;
            fo(int i=a;i<=bl[a]*block;i++) v[i]+=c;
        }
        else {
            for(int i=bl[a]+1;i<=bl[b]-1;i++) lab[i]+=c;
            fo(int i=a;i<=bl[a]*block;i++) v[i]+=c;
            fo(int i=(bl[b]-1)*block+1;i<=b;i++) v[i]+=c; 
        }
    }
}

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值