数列分块入门专题

分块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\n",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\n",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\n",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\n",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\n",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\n",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\n",(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\n",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\n",mp[ans]);
    } return 0;
}

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

posted @ 2018-09-13 22:17  kafuuchino  阅读(204)  评论(0编辑  收藏  举报