分块--解决区间问题

什么时候用分块?

当你发现正常数据结构无法解决的时候(比如维度特别多,很不方便或者炸空间),或者复杂到要3个 log 以上才能解决时。(主要还是得看数据范围,分块的做法一般都是 O(n) 及以上的

如何分块?

定一个块长 B ,整个序列就会被分成 \floorn/B 块,对于末尾的不完整的块,可以直接暴力修改查询

对于一个区间,会分解成两个不完整的块和中间的一些完整的块,那么我们就可以暴力不完整的块,对于完整的块我们只需要打个标记就可以了

 

例题:
P2801 教主的魔法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e6+10,mod=1e9+7; int a[N],e[N],be[N]; int L[N],R[N],tag[N]; void make(int x){ //对块进行操作,每个块都排序,那么我们在查询的时候就可以直接二分出最接近c的数,然后O(1)求出答案 for(int i=L[x];i<=R[x];i++) e[i]=a[i]; //由于a还在修改的时候有用,所以我们要另开一个数组 sort(e+L[x],e+R[x]+1); } signed main() { std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int n,q; cin>>n>>q; for(int i=1;i<=n;i++) cin>>a[i]; int len=sqrt(n),tot=(n+len-1)/len; //定义块长,分块 for(int i=1;i<=tot;i++){ L[i]=(i-1)*len+1,R[i]=min(n,i*len); //求出每个块的左端点和右端点 for(int j=L[i];j<=R[i];j++) be[j]=i; //be是belong,序列中的下标映射到块的下标 make(i); } while(q--){ char check; cin>>check; if(check=='M'){ int l,r,w; cin>>l>>r>>w; if(be[l]==be[r]){ //如果在同一个块,直接暴力 for(int i=l;i<=r;i++) a[i]+=w; make(be[l]); //修改完之后要更新 } else{ for(int i=l;i<=R[be[l]];i++) a[i]+=w; //两个不完整的块,暴力 for(int i=L[be[r]];i<=r;i++) a[i]+=w; make(be[l]),make(be[r]); for(int i=be[l]+1;i<=be[r]-1;i++) tag[i]+=w;//完整的块,标记 } } else{ int cnt=0,l,r,c; cin>>l>>r>>c; if(be[l]==be[r]) for(int i=l;i<=r;i++) cnt+=(a[i]+tag[be[i]]>=c); else{ for(int i=l;i<=R[be[l]];i++) cnt+=(a[i]+tag[be[i]]>=c); for(int i=L[be[r]];i<=r;i++) cnt+=(a[i]+tag[be[i]]>=c); for(int i=be[l]+1;i<=be[r]-1;i++) cnt+=e+1+R[i]-lower_bound(e+L[i],e+R[i]+1,c-tag[i]); } cout<<cnt<<endl; } } return 0; }

Farmer John's Favorite Function - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

由于从 k 这个位置开始,向后推的话,会发现,当我们推到 k+6 的时候基本上 ak 的修改的贡献就小于 1 了,也就是说,对于每个修改的 ak,他的影响范围最多到 k+6,可以把值设为 2e9 自己推一下就可以推出来,所以我们的块长至少是 6,但是考虑保险设为 10 也一定不错,此时对于每一次查询,我们直接修改 ak 所在的块,并且记录贡献,由于贡献是递减的,有可能在区间中的某个点之前他的贡献是 ek+1,所以我们要倒着推,我们设这个区分点为 fi,从前向后推,如果说存在 fj>=2e9 那么就无解,因为我们的数最大不超过 2e9,对于最后一块区间,我们直接暴力修改即可

#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e6+10,mod=1e9+7,inf=2e9; int n,q,tot; int a[N],be[N],L[N],R[N],f[N],e[N]; int sqrtt(int x){ int x_=sqrtl(x); while(x_*x_>x) x_--; while((x_+1)*(x_+1)<=x) x_++; return x_; } void make(int x){ //开方,由于考虑浮点数影响,可以求稳更新一下sqrt(x) if(x==tot) return; e[x]=0; for(int i=L[x];i<=R[x];i++) e[x]=sqrtt(a[i]+e[x]); f[x]=e[x]+1; for(int i=R[x];i>=L[x]&&f[x]!=inf;i--) f[x]=min(inf,f[x]*f[x]-a[i]); } signed main() { std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>q; for(int i=1;i<=n;i++) cin>>a[i]; int len=max((int)10,(int)sqrt(n)); tot=(n+len-1)/len; for(int i=1;i<=tot;i++){ L[i]=(i-1)*len+1,R[i]=min(n,i*len); for(int j=L[i];j<=R[i];j++) be[j]=i; make(i); } while(q--){ int k,x; cin>>k>>x; a[k]=x,make(be[k]); int res=0; for(int i=1;i<=tot-1;i++) res=e[i]+(res>=f[i]); for(int i=L[tot];i<=n;i++) res=sqrtt(res+a[i]); cout<<res<<endl; } return 0; }

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/18135420.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
-- --
点击右上角即可分享
微信分享提示