• 数列分块入门专题


    分块1~9传送门

    思想不写(baidu分块第一hzwer巨佬) 仅给出code

    attention:一定要分清楚位置 i 和 所在块 blo[ i ]!(倒在这好几次QAQ)

    分块1:区间加法,单点查值

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cctype>
    using namespace std;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[50002],atag[50002],a[50002]; //atag:加法标记
    inline void add(int l,int r,int k){
        for(int i=min(blo[l]*_blo,r);i>=l;--i) a[i]+=k; //左端破碎块
        for(int i=blo[l]+1;i<blo[r];++i) atag[i]+=k; //完整块
        if(blo[l]==blo[r]) return ;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) a[i]+=k; //右侧破碎块
    }
    int main(){
        scanf("%d",&n); _blo=sqrt(n); int opt,q1,q2,q3;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),blo[i]=(i-1)/_blo+1; //注意i-1
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&opt,&q1,&q2,&q3);
            if(opt==0) add(q1,q2,q3);
            else printf("%d
    ",a[q2]+atag[blo[q2]]);
        }
        return 0;
    }

    分块2:区间加法,询问区间内小于某个值的元素个数。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[50002],atag[50002],a[50002];
    vector <int> v[50002];
    inline void reset(int x){ //编号为x的块重新排序
        v[x].clear();
        for(int i=min(x*_blo,n);i>=(x-1)*_blo+1;--i) v[x].push_back(a[i]);
        sort(v[x].begin(),v[x].end());
    }
    inline void add(int l,int r,int k){
        for(int i=min(blo[l]*_blo,r);i>=l;--i) a[i]+=k;
        reset(blo[l]);
        for(int i=blo[l]+1;i<blo[r];++i) atag[i]+=k;
        if(blo[l]==blo[r]) return;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) a[i]+=k;
        reset(blo[r]);
    }
    inline int query(int l,int r,int k){
        int ans=0;
        for(int i=min(blo[l]*_blo,r);i>=l;--i) if(a[i]+atag[blo[i]]<k) ++ans;
        for(int i=blo[l]+1;i<blo[r];++i) ans+=lower_bound(v[i].begin(),v[i].end(),k-atag[i])-v[i].begin();
        if(blo[l]==blo[r]) return ans;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) if(a[i]+atag[blo[i]]<k) ++ans; 
        return ans;
    }
    int main(){
        scanf("%d",&n);
        _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),v[blo[i]=(i-1)/_blo+1].push_back(a[i]);
        for(int i=1;i<=blo[n];++i) sort(v[i].begin(),v[i].end()); //预处理
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4); 
            if(q1==0) add(q2,q3,q4);
            else printf("%d
    ",query(q2,q3,q4*q4));
        }
        return 0;
    }

    分块3:区间加法,询问区间内小于某个值 xxx 的前驱(比其小的最大元素)。

    只要把分块2的code稍作修改即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    inline int max(int &a,int &b) {return a>b ?a:b;}
    int n,_blo,blo[100002],atag[500],a[100002];
    vector <int> v[500];
    inline void reset(int x){
        v[x].clear();
        for(int i=min(x*_blo,n);i>=(x-1)*_blo+1;--i) v[x].push_back(a[i]);
        sort(v[x].begin(),v[x].end());
    }
    inline void add(int l,int r,int k){
        for(int i=min(blo[l]*_blo,r);i>=l;--i) a[i]+=k;
        reset(blo[l]);
        for(int i=blo[l]+1;i<blo[r];++i) atag[i]+=k;
        if(blo[l]==blo[r]) return ;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) a[i]+=k;
        reset(blo[r]);
    }
    inline int query(int l,int r,int k){
        int ans=-1;
        for(int i=min(blo[l]*_blo,r);i>=l;--i) if(a[i]+atag[blo[i]]<k) ans=max(ans,a[i]+atag[blo[i]]);
        for(int i=blo[l]+1;i<blo[r];++i){
            vector<int>::iterator it=lower_bound(v[i].begin(),v[i].end(),k-atag[i]); //注意用迭代器存
            if(it==v[i].begin()) continue; //找不到
            ans=max(ans,*(--it)+atag[i]);
        }
        if(blo[l]==blo[r]) return ans;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) if(a[i]+atag[blo[i]]<k) ans=max(ans,a[i]+atag[blo[i]]);
        return ans;
    }
    int main(){
        scanf("%d",&n); 
        _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),v[blo[i]=(i-1)/_blo+1].push_back(a[i]);
        for(int i=1;i<=blo[n];++i) sort(v[i].begin(),v[i].end());
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4);
            if(q1==0) add(q2,q3,q4);
            else printf("%d
    ",query(q2,q3,q4));
        }
        return 0;
    }

    分块4:区间加法,区间求和。

    注意超出int范围

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[50002];
    ll atag[300],a[50002],sum[300]; //注意atag和sum标记互不影响
    inline void add(int l,int r,int k){
        for(int i=min(blo[l]*_blo,r);i>=l;--i) a[i]+=k,sum[blo[i]]+=k;
        for(int i=blo[l]+1;i<blo[r];++i) atag[i]+=k;
        if(blo[l]==blo[r]) return ;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) a[i]+=k,sum[blo[i]]+=k;
    }
    inline ll query(int l,int r){
        ll ans=0;
        for(int i=min(blo[l]*_blo,r);i>=l;--i) ans+=a[i]+atag[blo[i]];
        for(int i=blo[l]+1;i<blo[r];++i) ans+=sum[i]+atag[i]*_blo;
        if(blo[l]==blo[r]) return ans;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) ans+=a[i]+atag[blo[i]];
        return ans;
    }
    int main(){
        scanf("%d",&n); _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%lld",&a[i]),sum[blo[i]=(i-1)/_blo+1]+=a[i];
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4);
            if(q1==0) add(q2,q3,q4);
            else printf("%lld
    ",query(q2,q3)%(q4+1));
        }
        return 0;
    }

    (on 09.13 好累啊qwq,明天再来吧)

    分块5:区间开方,区间求和。

    用一个stag标记表示某块是否已经全是0/1,就不用继续开方,可以跳过。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[50002],stag[300],sum[300],a[50002];
    inline void _sqrt(int l,int r){
        for(int i=min(blo[l]*_blo,r);i>=l;--i) sum[blo[i]]-=a[i],a[i]=sqrt(a[i]),sum[blo[i]]+=a[i]; //暴力开方
        for(int i=blo[l]+1;i<blo[r];++i){
            if(stag[i]) continue; //已经全是0/1,跳过
            stag[i]=1; sum[i]=0;
            for(int j=(i-1)*_blo+1;j<=i*_blo;++j)
                a[j]=sqrt(a[j]),sum[i]+=a[j],stag[i]= !stag[i]||a[j]>1 ? 0:1; //暴力开方
        }
        if(blo[l]==blo[r]) return ;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) sum[blo[i]]-=a[i],a[i]=sqrt(a[i]),sum[blo[i]]+=a[i]; //暴力开方
    //sum[blo[i]]-=a[i]-(a[i]=sqrt(a[i])); 我再也不敢这样写了qwq
    }
    inline int query(int l,int r){
        int ans=0;
        for(int i=min(blo[l]*_blo,r);i>=l;--i) ans+=a[i];
        for(int i=blo[l]+1;i<blo[r];++i) ans+=sum[i];
        if(blo[l]==blo[r]) return ans;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) ans+=a[i];
        return ans;
    }
    int main(){
        scanf("%d",&n); _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),sum[blo[i]=(i-1)/_blo+1]+=a[i];
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4);
            if(q1==0) _sqrt(q2,q3);
            else printf("%d
    ",query(q2,q3));
        } return 0;
    }

    分块6:单点插入,单点询问,数据随机生成。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    int n,_blo,a[200050],m; //m:存块的数量
    vector <int> v[1200];
    inline pair<int,int> query(int x){ //查找x在哪个块的哪个位置
        int p=1;
        while(x>v[p].size()) x-=v[p++].size();
        return make_pair(p,x-1);
    }
    inline void rebuild(){ //重新分块
        int tot=0;
        for(int i=1;i<=m;++i){
            for(vector<int>::iterator it=v[i].begin();it!=v[i].end();++it) a[++tot]=*it;
            v[i].clear();
        }
        _blo=sqrt(tot); m=(tot-1)/_blo+1;
        for(int i=1;i<=tot;++i) v[(i-1)/_blo+1].push_back(a[i]);
    }
    inline void _insert(int l,int k){
        pair<int,int> t=query(l);
        v[t.first].insert(v[t.first].begin()+t.second,k); //记得加上begin
        if(v[t.first].size()>20*_blo) rebuild();
    }
    int main(){ 
        scanf("%d",&n); _blo=sqrt(n); m=(n-1)/_blo+1; int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),v[(i-1)/_blo+1].push_back(a[i]);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4);
            if(q1==0) _insert(q2,q3);
            else {pair<int,int> t=query(q3);printf("%d
    ",v[t.first][t.second]);}
        }return 0;
    }

    分块7:区间乘法,区间加法,单点询问

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int mod=10007;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[100002],atag[500],mtag[500],a[100002];
    inline void reset(int x){ //暴力用掉标记
        for(int i=min(n,x*_blo);i>=(x-1)*_blo+1;--i) a[i]=(a[i]*mtag[x]+atag[x])%mod;
        atag[x]=0; mtag[x]=1;
    }
    inline void add(int f,int l,int r,int k){
        reset(blo[l]);
        for(int i=min(blo[l]*_blo,r);i>=l;--i) a[i]=(f?a[i]*k:a[i]+k)%mod;
        for(int i=blo[l]+1;i<blo[r];++i){
            if(f) atag[i]=atag[i]*k%mod,mtag[i]=mtag[i]*k%mod;
            else atag[i]=(atag[i]+k)%mod;
        } if(blo[l]==blo[r]) return ;
        reset(blo[r]);
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) a[i]=(f?a[i]*k:a[i]+k)%mod;
    }
    int main(){
        scanf("%d",&n); _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),blo[i]=(i-1)/_blo+1;
        for(int i=1;i<=blo[n];++i) mtag[i]=1;
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&q1,&q2,&q3,&q4);
            if(q1==2) printf("%d
    ",(a[q3]*mtag[blo[q3]]+atag[blo[q3]])%mod); 
            else add(q1,q2,q3,q4);
        } return 0;
    }

    分块8:区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,_blo,blo[100002],flag[1200],a[100002];
    inline void reset(int x){ //暴力用掉标记
        if(flag[x]==-1) return;
        for(int i=min(x*_blo,n);i>=(x-1)*_blo+1;--i) a[i]=flag[x];
        flag[x]=-1;
    }
    inline int modify(int l,int r,int k){
        int ans=0; reset(blo[l]);
        for(int i=min(blo[l]*_blo,r);i>=l;--i) ans= a[i]==k?ans+1:ans,a[i]=k;
        for(int i=blo[l]+1;i<blo[r];++i){
            if(flag[i]!=-1){
                if(flag[i]==k) ans+=_blo;
            }else for(int j=min(i*_blo,n);j>=(i-1)*_blo+1;--j) ans= a[j]==k?ans+1:ans,a[j]=k; //整块暴力修改
            flag[i]=k; 
        } if(blo[l]==blo[r]) return ans;
        reset(blo[r]);
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i) ans= a[i]==k?ans+1:ans,a[i]=k;
        return ans;
    }
    int main(){ 
        memset(flag,-1,sizeof(flag));
        scanf("%d",&n); _blo=sqrt(n); int q1,q2,q3,q4;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),blo[i]=(i-1)/_blo+1;
        for(int i=1;i<=n;++i) scanf("%d%d%d",&q1,&q2,&q3),printf("%d
    ",modify(q1,q2,q3));
        return 0;
    }

    (09.14 剩下一个明天来吧qwq)

     分块9:在线区间众数(然而跑蒲公英T的飞起)

    P4168 [Violet]蒲公英

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<map>
    #include<cctype>
    using namespace std;
    inline int Int(){
        char c=getchar(); int x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    inline int min(int &a,int &b) {return a<b ?a:b;}
    int n,ans,mxd,cnt,_blo,blo[50002],a[50002],f[505][505],mp[50002],ct[50002];
    vector <int> v[50002];
    map <int,int> Map;
    inline void pre(int x){
        memset(ct,0,sizeof(ct)); int mx=0,t=0;
        for(int i=(x-1)*_blo+1;i<=n;++i){
            ++ct[a[i]];
            if(ct[a[i]]>mx||(mx==ct[a[i]]&&mp[a[i]]<mp[t])) t=a[i],mx=ct[a[i]];
            f[x][blo[i]]=t;
        }
    }
    inline int find(int l,int r,int k){
        return upper_bound(v[k].begin(),v[k].end(),r)-lower_bound(v[k].begin(),v[k].end(),l);
    }
    inline void query(int l,int r){
        ans=f[blo[l]+1][blo[r]-1];
        mxd=find(l,r,ans);
        for(int i=min(blo[l]*_blo,r);i>=l;--i){
            int t=find(l,r,a[i]);
            if(t>mxd||(t==mxd&&mp[a[i]]<mp[ans])) mxd=t,ans=a[i];
        }
        if(blo[l]==blo[r]) return ;
        for(int i=(blo[r]-1)*_blo+1;i<=r;++i){
            int t=find(l,r,a[i]);
            if(t>mxd||(t==mxd&&mp[a[i]]<mp[ans])) mxd=t,ans=a[i];
        }
    }
    int main(){
        n=Int(); _blo=200; int q1,q2;
        for(int i=1;i<=n;++i){
            a[i]=Int();
            if(!Map[a[i]]) Map[a[i]]=++cnt,mp[cnt]=a[i];
            a[i]=Map[a[i]];
            v[a[i]].push_back(i);
            blo[i]=(i-1)/_blo+1;
        }
        for(int i=1;i<=blo[n];++i) pre(i);
        for(int i=1;i<=n;++i){
            q1=Int(); q2=Int();
            query(q1,q2);
            printf("%d
    ",mp[ans]);
        } return 0;
    }

    end.(终于结束了.....TAT)

  • 相关阅读:
    企业应用架构模式web表现层
    企业应用架构模式组织领域逻辑
    企业应用架构模式并发
    数据访问层
    企业应用架构模式映射到关系数据库
    企业应用架构模式引言
    领域驱动设计(精简版)
    企业应用架构模式分层
    服务层
    企业应用架构模式前言
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9643604.html
Copyright © 2020-2023  润新知