首页 > 代码库 > 分块学习

分块学习

分块是一种非常常用的技巧,他功能非常强大;然而相对的,它的时间复杂度较高,这也是它的缺陷。

设定每块大小为根号n,这样就把序列分为了n/根号n块

对于每一块,整体处理,不能构成一块的,暴力处理

 

代码:

    for(int i=1;i<=n;i++)    {        a[i]=read();belong[i]=(i-1)/t+1;        sum[belong[i]]+=a[i];    } 

这样就完成了分块 例如10个数,belong[i]分别为 1 1 1 2 2 2 3 3 3 4

 

修改或查询时分三段区间

1. L~min(belong[L],R),闭区间。

2. belong[L]+1~belong[R],半开半闭区间。

3.(belong[R]-1)*t+1~t,闭区间。

这样就OK啦 例如区间求和

代码:

int query(int x,int y){    ans=0;    for(int i=x;i<=min(y,belong[x]*t);i++) ans+=a[i];    for(int i=belong[x]+1;i<belong[y];i++) ans+=sum[i];    if(belong[x]!=belong[y]) for(int i=(belong[y]-1)*t+1;i<=y;i++) ans+=a[i];    return ans;}

 

以下3题练练手

T1 http://codevs.cn/problem/1080/ 线段树练习  

技术分享
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define N 100001using namespace std;int n,m,x,y,z,t,ans,a[N],belong[N],sum[N];inline int read(){    int x=0,f=1;char c=getchar();    while(c>9||c<0){if(c==-)f=-1;c=getchar();}    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}    return x*f;}int query(int x,int y){    ans=0;    for(int i=x;i<=min(y,belong[x]*t);i++) ans+=a[i];    for(int i=belong[x]+1;i<belong[y];i++) ans+=sum[i];    if(belong[x]!=belong[y]) for(int i=(belong[y]-1)*t+1;i<=y;i++) ans+=a[i];    return ans;}int main(){    n=read();t=sqrt(n);    for(int i=1;i<=n;i++)    {        a[i]=read();        belong[i]=(i-1)/t+1;        sum[belong[i]]+=a[i];    }    m=read();    for(int i=1;i<=m;i++)    {        x=read();y=read();z=read();        if(x==1){a[y]+=z;sum[belong[y]]+=z;}        if(x==2){printf("%d\n",query(y,z));}    }    return 0;}
单点修改,区间查询

T2 http://codevs.cn/problem/1081/ 线段树练习2

技术分享
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define N 100001using namespace std;int n,m,q,x,y,z,t,ans,a[N],belong[N],tot[N];inline int read(){    int x=0,f=1;char c=getchar();    while(c>9||c<0){if(c==-)f=-1;c=getchar();}    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}    return x*f;}int main(){    n=read();t=sqrt(n);    for(int i=1;i<=n;i++)    {        a[i]=read();        belong[i]=(i-1)/t+1;    }    m=read();    for(int i=1;i<=m;i++)    {        x=read();        if(x==1)        {            y=read();z=read();q=read();            for(int i=y;i<=min(z,belong[y]*t);i++) a[i]+=q;            for(int i=belong[y]+1;i<belong[z];i++) tot[i]+=q;            if(belong[y]!=belong[z])for(int i=(belong[z]-1)*t+1;i<=z;i++) a[i]+=q;        }        if(x==2)        {            y=read();            printf("%d\n",a[y]+tot[belong[y]]);        }    }    return 0;}
单点查询,区间修改

T3 http://codevs.cn/problem/1082/ 线段树练习3 

技术分享
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define N 200001using namespace std;long long n,m,x,y,z,t,w,R;long long sum[N],flag[N],ans,a[N],belong[N];inline long long read(){    int x=0,f=1;char c=getchar();    while(c>9||c<0){if(c==-)f=-1;c=getchar();}    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}    return x*f;}int main(){    n=read();t=sqrt(n);     for(int i=1;i<=n;i++)    {        a[i]=read();belong[i]=(i-1)/t+1;        sum[belong[i]]+=a[i];    }     m=read();    for(int i=1;i<=m;i++)    {        w=read();        if(w==1)        {            x=read();y=read();z=read();R=min(y,belong[x]*t);            for(int i=x;i<=R;i++) a[i]+=z,sum[belong[i]]+=z;            for(int i=belong[x]+1;i<belong[y];i++) flag[i]+=z;            if(belong[x]!=belong[y])             for(int i=(belong[y]-1)*t+1;i<=y;i++) a[i]+=z,sum[belong[i]]+=z;        }        else        {            ans=0;            x=read();y=read();R=min(y,belong[x]*t);            for(int i=x;i<=R;i++) ans+=a[i]+flag[belong[i]];            for(int i=belong[x]+1;i<belong[y];i++) ans+=sum[i]+flag[i]*t;            if(belong[x]!=belong[y])             for(int i=(belong[y]-1)*t+1;i<=y;i++) ans+=a[i]+flag[belong[i]];            printf("%lld\n",ans);        }    }    return 0;}
区间修改+区间查询

 

分块学习