• 浅谈分块思想


    分块算法实质上是一种是通过分成多块后在每块上打标记以实现快速区间修改,区间查询的一种算法。其均摊时间复杂度为 O(√ n)

    分块算法相较于各种树形数据结构,具有简便易写,方便调试等多种优点。在同等数据规模下,如 1e5 ,其时间效率并不会低太多,在考试时反而是一种有力的得分方法。

    但是若是数据量到1s/5e5以上,基本上就会T…

    分块的实质

    分块其实是一种树形结构,它是一种只有三层的树,形态如下:
    在这里插入图片描述
    下面给出几种分块常用板子:
    1.区间加法,单点查询

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 500005
    #define ll long long
    #define rint register int
    
    int n,m,blo;
    int v[maxn],bl[maxn],sum[maxn];
    
    void add(int a,int b,int c){
        for(rint i=a;i<=min(bl[a]*blo,b);i++)
            v[i]+=c;
        if(bl[a]!=bl[b])
            for(rint i=(bl[b]-1)*blo+1;i<=b;i++)
                v[i]+=c;
        for(rint i=bl[a]+1;i<=bl[b]-1;i++)
            sum[i]+=c;
    }
    
    int main()
    {
        scanf("%d %d",&n,&m);
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
            bl[i]=(i-1)/blo+1;
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        while(m--){
            int op,a,b,c;
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %d",&a,&b,&c);
                add(a,b,c);
            }
            else{
                scanf("%d",&a);
                printf("%d
    ",v[a]+sum[bl[a]]);
            }
        }
        return 0;
    }

    这份代码能过5e5数据量的 洛谷P3368 【模板】树状数组 2…

    2.区间加法,区间求和

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    #define ll long long
    
    int n,m,blo;
    int v[maxn],bl[maxn],atag[maxn];
    ll sum[maxn];
    
    void add(int a,int b,int c){
        for(int i=a;i<=min(bl[a]*blo,b);i++){
            v[i]+=c;
            sum[bl[a]]+=c;
        }
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++){
                v[i]+=c;
                sum[bl[b]]+=c;
            }
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            atag[i]+=c;
    }
    
    ll getsum(int a,int b){
        ll ans=0;
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            ans+=v[i]+atag[bl[a]];
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                ans+=v[i]+atag[bl[b]];
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            ans+=sum[i]+atag[i]*blo;
        return ans;
    }
    
    int main()
    {
        scanf("%d %d",&n,&m);
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int i=1;i<=n;i++){
            bl[i]=(i-1)/blo+1;
            sum[bl[i]]+=v[i];
        }    
        while(m--){
            int op,a,b,c;
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %d",&a,&b,&c);
                add(a,b,c);
            }
            else{
                scanf("%d %d",&a,&b);
                printf("%lld
    ",getsum(a,b));
            }
        }
        return 0;
    }

    3.区间加法,区间查小于X的个数

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    #define ll long long
    
    int n,m,blo;
    int v[maxn],bl[maxn],atag[maxn];
    vector<int>ve[505];
    void reset(int x)
    {
        ve[x].clear();
        for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
            ve[x].push_back(v[i]);
        sort(ve[x].begin(),ve[x].end());
    }
    void add(int a,int b,int c)
    {
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            v[i]+=c;
        reset(bl[a]);
        if(bl[a]!=bl[b]){
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                v[i]+=c;
            reset(bl[b]);
        }
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            atag[i]+=c;
    }
    int query(int a,int b,int c)
    {
        int ans=0;
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            if(v[i]+atag[bl[a]]<c)ans++;
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                if(v[i]+atag[bl[b]]<c)ans++;
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
        {
            int x=c-atag[i];
            ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin();
        }
        return ans;
    }
    int main()
    {
        scanf("%d %d",&n,&m);
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int i=1;i<=n;i++)
        {
            bl[i]=(i-1)/blo+1;
            ve[bl[i]].push_back(v[i]);
        }
        for(int i=1;i<=bl[n];i++)
            sort(ve[i].begin(),ve[i].end());
        for(int i=1;i<=m;i++)
        {
            int f,a,b,c;
            scanf("%d %d %d %d",&f,&a,&b,&c);
            if(f==0)add(a,b,c);
            if(f==1)printf("%d
    ",query(a,b,c));
        }
        return 0;
    }
    /*
    6 5
    1 5 6 4 2 3
    0 1 4 3
    1 1 6 4
    0 1 2 1
    1 1 6 9
    1 5 6 1
    */

    4.区间加乘,单点查值

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    #define ll long long
    
    int n,m,blo;
    int v[maxn],bl[maxn],atag[maxn],mtag[maxn];
    
    void add(int a,int b,int c)
    {
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            v[i]+=c;
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                v[i]+=c;
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            atag[i]+=c;
    }
    
    void muti(int a,int b,int c)
    {
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            v[i]*=c;
        atag[bl[a]]*=c;
        if(bl[a]!=bl[b]){
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                v[i]*=c;
            atag[bl[b]]*=c;
        }
        for(int i=bl[a]+1;i<=bl[b]-1;i++){
            atag[i]*=c;
            mtag[i]*=c;
        }
    }
    
    int main()
    {
        scanf("%d %d",&n,&m);
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(int i=1;i<=n;i++)
            bl[i]=(i-1)/blo+1;
        for(int i=1;i<=sqrt(n)+1;i++)
            mtag[i]=1;
        while(m--){
            int op,a,b,c;
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %d",&a,&b,&c);
                add(a,b,c);
            }
            if(op==2){
                scanf("%d %d %d",&a,&b,&c);
                muti(a,b,c);
            }
            if(op==3){
                scanf("%d",&a);
                printf("%lld
    ",1ll*v[a]*mtag[bl[a]]+atag[bl[a]]);
            }
        }
    }

    5.区间异或,区间求值

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    #define ll long long
    
    int n,m,blo;
    int v[maxn],bl[maxn],t[maxn];
    ll atag[maxn];
    
    void chang(int a,int b){
        for(int i=a;i<=min(bl[a]*blo,b);i++){
            atag[bl[a]]-=(v[i]^t[bl[a]]);
            v[i]^=1;
            atag[bl[a]]+=(v[i]^t[bl[a]]);
        }
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++){
                atag[bl[b]]-=(v[i]^t[bl[b]]);
                v[i]^=1;
                atag[bl[b]]+=(v[i]^t[bl[b]]);
            }
        for(int i=bl[a]+1;i<=bl[b]-1;i++){
            atag[i]=blo-atag[i];
            t[i]^=1;
        }
    }
    
    ll query(int a,int b){
        int ans=0;
        for(int i=a;i<=min(bl[a]*blo,b);i++)
            ans+=1ll*v[i]^t[bl[a]];
        if(bl[a]!=bl[b])
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
                ans+=1ll*v[i]^t[bl[b]];
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            ans+=1ll*atag[i];
        return ans;
    }
    
    int main()
    {
        scanf("%d %d",&n,&m);
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
            bl[i]=(i-1)/blo+1;
        while(m--){
            int op,a,b;
            scanf("%d %d %d",&op,&a,&b);
            if(op==0)chang(a,b);
            else printf("%lld
    ",query(a,b));
        }
        return 0;
    }
    希望用自己的努力为自己赢得荣誉。
  • 相关阅读:
    perl 实现ascall 码转换
    perl 利用管道读取压缩文件内容
    perl 字符串比较操作符
    perl chomp 函数的真正作用
    RSQLite 操作sqlite数据库
    R 中的do.call 函数
    JavaMail发送和接收邮件API(详解)
    POP3_使用SSL链接邮箱并获取邮件
    MySql_delete同时删除多表相关联记录
    mybatis_mybatis写mapper文件注意事项
  • 原文地址:https://www.cnblogs.com/Mmasker/p/11917482.html
Copyright © 2020-2023  润新知