• 分块————优雅的暴力


    我认为,分块是一种比较巧妙,简洁易懂的算法,简称暴力

    它主要的思想是将一串数列拆成sqrt(n)块,那么显然可以得出共有sqrt(n)个

    将一个块可以看成一个整体,所包含的数可以同时变化,这样时间复杂度就会大大的减小

    那怎样变化呢?        举个栗子

    3    3    6    3    7    5    5    4    7    3    4    

    共有十一个数(如上)

    那么就可以分成4分(最后会有不足sqrt(n)的,也算一块)

    3    3    6————1

    3    7    5————2

    5    4    7————3

    3    4      ————4

    如果要将2—-11每一个数加m,那么我们可以先得出2属于第一块,11属于第四块

    它们中间的第三块和第二块均加m,可以用数组储存每一块整体需要加的数,这里用add[ ],

    所以,add[2]+=m,add[3]+=m;如果后面要用的话,可以直接调用,这样就节约了很多时间

    但是还是有些边边角角的数,那这个有一个巧妙的方法,暴力,是的,就是暴力,没有任何花样的暴力

    ————————————————————————————————————————————————

    好的,分块基本思想讲完了,那该怎么建立块呢  

    void make_block(){
        T=sqrt(number);
        for(int i=1;i<=T;i++)L[i]=(i-1)*sqrt(number)+1,R[i]=i*sqrt(number);
        if(R[T]<number)T++,R[T]=number,L[T]=R[T-1]+1;
        for(int i=1;i<=T;i++){
            for(int j=L[i];j<=R[i];j++)
               pos[j]=i,sum[i]+=num[j];
        }
    }

    pos存的是每一个数所在块,sum是一个块中数的和

    那经过修改以后,对于第i个块,它的和为:    sum[i]+add[i]

    由此,求某一区间数的和也更加方便

     

    现有一道模板题:洛谷p3372【模板】线段树1  

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1.将某区间每一个数加上x

    2.求出某区间每一个数的和

    输入输出格式

    输入格式:

     

    第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

    接下来M行每行包含3或4个整数,表示一个操作,具体如下:

    操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

    操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

     

    输出格式:

     

    输出包含若干行整数,即为所有操作2的结果。

    输入输出样例

    输入样例#1: 
    5 5
    1 5 4 2 3
    2 2 4
    1 2 3 2
    2 3 4
    1 1 5 1
    2 1 4
    输出样例#1: 
    11
    8
    20

    很显然,上述两个操作基本思路已经讲述过了
    下面是题解:
    //分块 
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=1000010;
    ll number,num[N],pos[N],add[N],T,L[N],R[N],need,sum[N];
    
    long long read(){
        long long s=0,w=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
        return s*w;
    }
    
    void make_block(){
        T=sqrt(number);
        for(int i=1;i<=T;i++)L[i]=(i-1)*sqrt(number)+1,R[i]=i*sqrt(number);
        if(R[T]<number)T++,R[T]=number,L[T]=R[T-1]+1;
        for(int i=1;i<=T;i++){
            for(int j=L[i];j<=R[i];j++)
               pos[j]=i,sum[i]+=num[j];
        }
    }
    
    void change(ll x,ll y,ll d){
        ll p=pos[x],q=pos[y];
        if(p==q){
            for(int i=x;i<=y;i++)num[i]+=d;
            sum[q]+=(y-x+1)*d;
        }
        else{
            for(int i=p+1;i<=q-1;i++)add[i]+=d;
            for(int i=x;i<=R[p];i++)num[i]+=d;
            sum[p]+=d*(R[p]-x+1);
            for(int i=L[q];i<=y;i++)num[i]+=d;
            sum[q]+=d*(y-L[q]+1);
        }
    }
    
    ll ask(int x,int y){
        ll p=pos[x],q=pos[y],ans=0;
        if(p==q){
            for(int i=x;i<=y;i++)ans+=num[i];
            ans+=add[p]*(y-x+1);
        }
        else{
            for(int i=p+1;i<=q-1;i++)ans+=sum[i]+add[i]*(R[i]-L[i]+1);
            for(int i=x;i<=R[p];i++)ans+=num[i];
            ans+=add[p]*(R[p]-x+1);
            for(int i=L[q];i<=y;i++)ans+=num[i];
            ans+=add[q]*(y-L[q]+1);
        }
        return ans;
    }
    
    int main(){
        number=read();need=read();
        for(int i=1;i<=number;i++)num[i]=read();
        make_block();
        for(int i=1;i<=need;i++){
            ll pd=read(),x=read(),y=read(),d;
            if(pd==1){
                d=read();change(x,y,d);
            }
            else {
                cout<<ask(x,y)<<endl;
            }
        }
        return 0;
    }
  • 相关阅读:
    [编写高质量代码:改善java程序的151个建议]建议72 生成字列表后不要再操作原列表
    [编写高质量代码:改善java程序的151个建议]建议71 推荐使用subList处理局部列表
    [编写高质量代码:改善java程序的151个建议]建议70 子列表只是原列表的一个视图
    程序员的简历到底该怎么写?(转)
    SQL数据库数据优化SQL优化总结( 百万级数据库优化方案)
    sqlserver的四种分页方式
    sql server中截取字符串的常用函数(自己经常到用的时候想不起来所以拿到这里)
    SQL之存储过程详细介绍及语法(篇幅比较长慢慢看)
    超经典SQL练习题,做完这些你的SQL就过关了
    SqlServer 数据库引擎优化顾问优化数据库(消耗内存很大)
  • 原文地址:https://www.cnblogs.com/GMSD/p/11217964.html
Copyright © 2020-2023  润新知