一转眼,已经有整整一年没有在博客园写博客了。去洛谷写了几篇(How time flys.
最近突然想起其实我不太擅长分块算法,又想起去年暑假有位同学同我提起过LOJ的数列分块九题,说来惭愧,打了这么久题今天才打算做这个系列,甚至连LOJ的账号都没有注册。。。于是今天花费了半天时间敲题,但是更尴尬的是只打了前四题,分块细节真的是相当繁琐。
分块是一种优雅的暴力,时间复杂度为根号n量级,虽然同线段树树状数组之类的logn量级的复杂度无法相提并论,但是在大多数情况下,分块都可以比较好地替代线段树。其代码量较少且逻辑相较线段树而言更清晰尤其是可以进行块内二分之类的操作,从而有的时候比线段树更有优势。
分块,顾名思义,就是将数列分为多块进行考虑,在进行区间修改或者区间查询操作时,使用分块算法,我们可以直接处理区间中的块,再处理零散块。我们可以将数列划分为根号n个块,从而将时间复杂度降低至根号n。
#6277. 数列分块入门 1 - 题目 - LibreOJ (loj.ac)
区间加法,单点查值,分块水题(变量名想半天)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=(5e4)+5; ll a[maxn]; ll l[maxn]; ll r[maxn]; ll bel[maxn]; ll lazy_add[maxn]; void build(ll n){ ll block=sqrt(n); ll num=n/block; if(n%block)num++; for(int i=1;i<=num;i++){ l[i]=(i-1)*block+1; r[i]=min(n,i*block); lazy_add[i]=0; } for(int i=1;i<=n;i++){ bel[i]=(i-1)/block+1; } } void add(ll L,ll rr,ll c){ ll s=bel[L]+1; ll t=bel[rr]-1; for(int i=L;i<=min(rr,r[s-1]);i++){ a[i]+=c; } if(bel[L]==bel[rr])return; for(int i=l[t+1];i<=rr;i++){ a[i]+=c; } for(int i=s;i<=t;i++){ lazy_add[i]+=c; } } void calc(ll x){ ll p=bel[x]; for(int i=l[p];i<=r[p];i++){ a[i]+=lazy_add[p]; } lazy_add[p]=0; cout<<a[x]<<endl; } int main(){ ll n; cin>>n; build(n); for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++){ ll opt,L,rr,c; cin>>opt>>L>>rr>>c; if(!opt){ add(L,rr,c); } else{ calc(rr); } } return 0; }
#6278. 数列分块入门 2 - 题目 - LibreOJ (loj.ac)
块内二分即可(线段树不行),注意边角的处理
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=(5e4)+5; int L[maxn],R[maxn]; int bel[maxn]; ll a[maxn]; ll b[maxn]; ll tmp[maxn]; ll tag[maxn]; void build(int n){ int block=sqrt(n); int num=n/block; if(n%block)num++; for(int i=1;i<=num;i++){ L[i]=(i-1)*block+1; R[i]=min(n,i*block); sort(a+L[i],a+R[i]+1); for(int j=L[i];j<=R[i];j++){ bel[j]=i; } } } void update(int l,int r,ll c){ if(bel[l]==bel[r]){ for(int i=l;i<=r;i++)b[i]+=c; for(int i=L[bel[l]];i<=R[bel[l]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[l]],tmp+R[bel[l]]+1); for(int i=L[bel[l]];i<=R[bel[l]];i++){ a[i]=tmp[i]; } } else{ for(int i=l;i<=R[bel[l]];i++){ b[i]+=c; } for(int i=L[bel[l]];i<=R[bel[l]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[l]],tmp+R[bel[l]]+1); for(int i=L[bel[l]];i<=R[bel[l]];i++){ a[i]=tmp[i]; } for(int i=L[bel[r]];i<=r;i++){ b[i]+=c; } for(int i=L[bel[r]];i<=R[bel[r]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[r]],tmp+R[bel[r]]+1); for(int i=L[bel[r]];i<=R[bel[r]];i++){ a[i]=tmp[i]; } for(int i=bel[l]+1;i<bel[r];i++){ tag[i]+=c; } } } void calc(int l,int r,ll c){ ll ans=0; if(bel[l]==bel[r]){ for(int i=l;i<=r;i++){ if(b[i]+tag[bel[l]]<c)ans++; } } else{ for(int i=l;i<=R[bel[l]];i++){ if(b[i]+tag[bel[l]]<c)ans++; } for(int i=L[bel[r]];i<=r;i++){ if(b[i]+tag[bel[r]]<c)ans++; } for(int i=bel[l]+1;i<bel[r];i++){ ans+=lower_bound(a+L[i],a+R[i]+1,c-tag[i])-a-L[i]; } } cout<<ans<<endl; } int main(){ int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i]; build(n); for(int i=1;i<=n;i++){ ll opt,l,r,c; cin>>opt>>l>>r>>c; if(!opt){ update(l,r,c); } else{ calc(l,r,c*c); } } return 0; }
#6279. 数列分块入门 3 - 题目 - LibreOJ (loj.ac)
前一题修改一下即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=(1e5)+5; int L[maxn],R[maxn]; int bel[maxn]; ll a[maxn]; ll b[maxn]; ll tmp[maxn]; ll tag[maxn]; void build(int n){ int block=sqrt(n); int num=n/block; if(n%block)num++; for(int i=1;i<=num;i++){ L[i]=(i-1)*block+1; R[i]=min(n,i*block); sort(a+L[i],a+R[i]+1); for(int j=L[i];j<=R[i];j++){ bel[j]=i; } } } void update(int l,int r,ll c){ if(bel[l]==bel[r]){ for(int i=l;i<=r;i++)b[i]+=c; for(int i=L[bel[l]];i<=R[bel[l]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[l]],tmp+R[bel[l]]+1); for(int i=L[bel[l]];i<=R[bel[l]];i++){ a[i]=tmp[i]; } } else{ for(int i=l;i<=R[bel[l]];i++){ b[i]+=c; } for(int i=L[bel[l]];i<=R[bel[l]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[l]],tmp+R[bel[l]]+1); for(int i=L[bel[l]];i<=R[bel[l]];i++){ a[i]=tmp[i]; } for(int i=L[bel[r]];i<=r;i++){ b[i]+=c; } for(int i=L[bel[r]];i<=R[bel[r]];i++){ tmp[i]=b[i]; } sort(tmp+L[bel[r]],tmp+R[bel[r]]+1); for(int i=L[bel[r]];i<=R[bel[r]];i++){ a[i]=tmp[i]; } for(int i=bel[l]+1;i<bel[r];i++){ tag[i]+=c; } } } void calc(int l,int r,ll c){ ll ans=-1e9; bool fg=0; if(bel[l]==bel[r]){ for(int i=l;i<=r;i++){ if(b[i]+tag[bel[l]]<c)ans=max(ans,b[i]+tag[bel[l]]),fg=1; } } else{ for(int i=l;i<=R[bel[l]];i++){ if(b[i]+tag[bel[l]]<c)ans=max(ans,b[i]+tag[bel[l]]),fg=1; } for(int i=L[bel[r]];i<=r;i++){ if(b[i]+tag[bel[r]]<c)ans=max(ans,b[i]+tag[bel[r]]),fg=1; } for(int i=bel[l]+1;i<bel[r];i++){ int p=lower_bound(a+L[i],a+R[i]+1,c-tag[i])-a-1; if(a[p]+tag[i]<c)ans=max(ans,a[p]+tag[i]),fg=1; } } if(fg)cout<<ans<<endl; else cout<<-1<<endl; } int main(){ int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i]; build(n); for(int i=1;i<=n;i++){ ll opt,l,r,c; cin>>opt>>l>>r>>c; if(!opt){ update(l,r,c); } else{ calc(l,r,c); } } return 0; }
#6280. 数列分块入门 4 - 题目 - LibreOJ (loj.ac)
区间加法,区间求和(经典线段树,不过也可以分块)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=(1e5)+5; ll a[maxn]; int L[maxn],R[maxn]; int bel[maxn]; ll siz[maxn]; ll tag[maxn]; void build(int n){ int block=sqrt(n); int num=n/block; if(n%block)num++; for(int i=1;i<=num;i++){ L[i]=(i-1)*block+1; R[i]=min(n,i*block); for(int j=L[i];j<=R[i];j++){ bel[j]=i; siz[i]+=a[j]; } } } void update(int l,int r,ll c){ if(bel[l]==bel[r]){ for(int i=l;i<=r;i++){ a[i]+=c; siz[bel[l]]+=c; } } else{ for(int i=l;i<=R[bel[l]];i++){ a[i]+=c; siz[bel[l]]+=c; } for(int i=L[bel[r]];i<=r;i++){ a[i]+=c; siz[bel[r]]+=c; } for(int i=bel[l]+1;i<bel[r];i++){ siz[i]+=(R[i]-L[i]+1)*c; tag[i]+=c; } } } void calc(int l,int r,ll c){ ll ans=0; if(bel[l]==bel[r]){ for(int i=l;i<=r;i++){ ans=(ans+a[i]+tag[bel[l]])%c; } } else{ for(int i=l;i<=R[bel[l]];i++){ ans=(ans+a[i]+tag[bel[l]])%c; } for(int i=L[bel[r]];i<=r;i++){ ans=(ans+a[i]+tag[bel[r]])%c; } for(int i=bel[l]+1;i<bel[r];i++){ ans=(ans+siz[i])%c; } } cout<<ans%c<<endl; } int main(){ int n; cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; build(n); for(int i=1;i<=n;i++){ ll opt,l,r,c; cin>>opt>>l>>r>>c; if(!opt){ update(l,r,c); } else calc(l,r,c+1); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧