分块学习小结1

对于分块学习,主要是因为今天有位大牛Tom栋爷在讲根号级别复杂度的算法时,就是以分块来作为基础讲的,然而因为当时我并不懂分块,所以在听讲是一脸懵逼。晚上赶紧恶补一番,入了门,刷了道题。


进入正题

分块,就是将一段信息(数据结构,序列)分成几块处理,当然最基础的是序列上的分块,再高层点的就是树上的分块,分块套分块,分块套数据结构以及数据结构套分块。
因为我是初学者,今天就记录下最基础的分块中的最基础的部分,好让自己以后忘了可以看看自己的博客。


用题目来记录是最有效的

给一个长度为n的序列,每次会给其一段区间增加一个值,然后单点查询。

解法一:线段树,区间修改与单点查询,时间复杂度:每次查询修改 O(logn) +建树 O(nlogn)
解法二:树状数组,经典的改段求点,时间复杂度为每次查询修改 O(logn) Segmen Tree来讲,常数较小。

当然了今天的重点应该是用分块来解决。


分块解法

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

分块:

        block=sqrt(n); //每一个块的个数。
        fo(i,1,n) bl[i]=(i-1)/block+1; //bl数组表示第i个位置属于的块是哪个
        bl[0]=1; bl[n+1]=bl[n]; //我会处理边界
        fo(i,1,n){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y); //区间修改
        }
        fo(i,1,n-1) 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 fo(i,a,b) v[i]+=c;
    }
    else {
        if (bz1 && bz2) 
            fo(i,bl[a],bl[b]) lab[i]+=c;
        else if (bz1 && !bz2) {
            fo(i,bl[a],bl[b]-1) lab[i]+=c;//整块打下标记
            fo(i,(bl[b]-1)*block+1,b) v[i]+=c;//多余部分直接暴力修改
        }
        else if (!bz1 && bz2) {
            fo(i,bl[a]+1,bl[b]) lab[i]+=c;
            fo(i,a,bl[a]*block) v[i]+=c;
        }
        else {
            fo(i,bl[a]+1,bl[b]-1) lab[i]+=c;
            fo(i,a,bl[a]*block) v[i]+=c;
            fo(i,(bl[b]-1)*block+1,b) v[i]+=c; 
        }
    }
}

实题:hdu1556

题意很明显,就不多说了。
这道题是可以由上述方法解决,涉及到区间修改与单点查询,套上模板即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=1e5+5;
int bl[maxn],a[maxn],lab[350],n,block;

void add(int x,int y)
{
    bool bz1,bz2;
    if (bl[x]==bl[x-1]) bz1=false; else bz1=true;
    if (bl[y]==bl[y+1]) bz2=false; else bz2=true;
    if (bl[x]==bl[y]){
        if (bz1 && bz2) ++lab[bl[x]];
        else fo(i,x,y) ++a[i];
    }
    else {
        if (bz1 && bz2) fo(i,bl[x],bl[y]) ++lab[i];
        else if (bz1 && !bz2) {
            fo(i,bl[x],bl[y]-1) lab[i]++;
            fo(i,(bl[y]-1)*block+1,y) ++a[i];
        }
        else if (!bz1 && bz2) {
            fo(i,bl[x]+1,bl[y]) lab[i]++;
            fo(i,x,bl[x]*block) ++a[i];
        }
        else {
            fo(i,bl[x]+1,bl[y]-1) lab[i]++;
            fo(i,x,bl[x]*block) ++a[i];
            fo(i,(bl[y]-1)*block+1,y) ++a[i];
        }
    }
}
int main()
{
    scanf("%d",&n);
    while (n){
        block=sqrt(n);
        fo(i,1,n) bl[i]=(i-1)/block+1;
        bl[0]=1; bl[n+1]=bl[n];
        fo(i,1,n){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        fo(i,1,n-1) printf("%d ",a[i]+lab[bl[i]]);//差点被格式坑死- -|||
        printf("%d\n",a[n]+lab[bl[n]]);
        fo(i,1,n+1){
            bl[i]=0;
            a[i]=0;

        }
        scanf("%d",&n);
        memset(lab,0,sizeof(lab));
    }   
}

分块还有很多需要学习,今天只是学习了一点,下次接着学习。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值