分块学习小结1

来源:互联网 发布:暖暖环游世界人工智能 编辑:程序博客网 时间:2024/06/16 20:33

对于分块学习,主要是因为今天有位大牛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));    }   }

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

0 0
原创粉丝点击