zoukankan      html  css  js  c++  java
  • 数列分块入门专题

    分块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)

  • 相关阅读:
    Centos 7 zabbix 实战应用
    Centos7 Zabbix添加主机、图形、触发器
    Centos7 Zabbix监控部署
    Centos7 Ntp 时间服务器
    Linux 150命令之查看文件及内容处理命令 cat tac less head tail cut
    Kickstart 安装centos7
    Centos7与Centos6的区别
    Linux 150命令之 文件和目录操作命令 chattr lsattr find
    Linux 发展史与vm安装linux centos 6.9
    Linux介绍
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9643604.html
Copyright © 2011-2022 走看看