数列分块入门专题
思想不写(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的飞起)
#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)