zl程序教程

您现在的位置是:首页 >  工具

当前栏目

分块 学习笔记

笔记学习 分块
2023-06-13 09:12:48 时间

分块 学习笔记

前言

忽然发现分块大法很好用,然而本蒟蒻不会…所以心血来潮学习了分块

例题

Code

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;

#define re register
#define It std::set<int>::iterator
#define int long long 

struct FastIO{
    static const int S=1048576;
    char buf[S],*L,*R;int stk[20],Top;
    ~FastIO(){clear();}
    inline char nc(){return L==R&&(R=(L=buf)+fread(buf,1,S,stdin),L==R)?EOF:*L++;}
    inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
    inline void pc(char ch){Top==S&&(clear(),0);buf[Top++]=ch;}
    inline void endl(){pc('\n');}
    FastIO& operator >> (int& ret){
        ret=0;int f=1;char ch=nc();
        while(ch<'0'ch>'9'){if(ch=='-')f=-f;ch=nc();}
        while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=nc();}
        ret*=f;return *this;
    }
    FastIO& operator >> (char* s){
        re int Len=0;re char ch=nc();
        while(ch!='\n'){
            *(s+Len)=ch;
            Len++;
            ch=nc();
        }
    }
    FastIO& operator << (int x){
        if(x<0){pc('-');x=-x;}
        do{stk[++stk[0]]=x%10;x/=10;}while(x);
        while(stk[0]) pc('0'+stk[stk[0]--]);
        return *this;
    }
    FastIO& operator << (char ch){pc(ch);return *this;}
    FastIO& operator << (string str){
        re int Len=str.size()-1;for(stk[0]=0;Len>=0;Len--) stk[++stk[0]]=str[Len];
        while(stk[0]) pc(stk[stk[0]--]);
        return *this;
    }
}fin,fout;
int n,m,ans,a[100010],lef[100010],rig[100010],id[100010],sum[100010],add[100010],tot,sz;
#define File freopen("1.in","r",stdin);freopen("1.out","w",stdout);
inline void Print(){
    for(int i=1;i<=tot;i++){
        cout<<i<<":"<<lef[i]<<" "<<rig[i]<<" "<<add[i]<<" "<<sum[i]<<endl;
    }
}
signed main(){
    fin>>n>>m;
    for(int i=1;i<=n;i++) fin>>a[i];
    sz=sqrt(n);tot=sz;//每个块大小为sqrt(n)
    if(sz*sz<=n) ++tot;//还有多余
    for(int i=1;i<=n;i++){
        id[i]=(i/sz)+(i%sz==0?0:1);//每个数所属的块的编号
        sum[id[i]]+=a[i];//每个块内的和
        if(id[i]==tot) lef[i]=sz*sz,rig[i]=n;//确定最后一个块的左右端点
        lef[id[i]]=id[i]*sz-sz+1;rig[id[i]]=id[i]*sz;//确定这个块的左右端点
    }
    for(int op,l,r,v,i=1;i<=m;i++){
        fin>>op>>l>>r;
        if(op==1){
            fin>>v;
            for(int i=l;i<=min(r,rig[id[l]]);i++) a[i]+=v,sum[id[i]]+=v;//把左边的暴力加入
            for(int i=max(l,lef[id[r]]);i<=r;i++) a[i]+=v,sum[id[i]]+=v;//把右边的暴力加入
            for(int i=id[l]+1;i<=id[r]-1;i++) add[i]+=v;//把中间的加入
            if(id[l]==id[r]) for(int i=l;i<=r;i++) a[i]-=v;//注意当l,r在同一个块内情况
        }else{
            ans=0;
            for(int i=l;i<=min(r,rig[id[l]]);i++) ans+=a[i]+add[id[i]];//同上
            for(int i=max(l,lef[id[r]]);i<=r;i++) ans+=a[i]+add[id[i]];//同上
            for(int i=id[l]+1;i<=id[r]-1;i++) ans+=sum[i]+add[i]*(rig[i]-lef[i]+1);//同上
            if(id[l]==id[r]) ans-=a[l]+a[r]+add[id[l]]+add[id[r]];//注意当l,r在同一个块内情况
            fout<<ans<<"\n";
        }
    }
}