分块学习笔记
分块是一种 思想
它用于存在区间问题,并且结合律不是简单相加的时候
线段树和树状数组此时不可用,这个时候,我们就要使用分块
切入正题
分块思想是这样的
设数组长度是n,共有q块,把数组存下来,分成
很类似线段树的"标记永久化",思想都是一样的,不过实现方法有很大差别。
而对于不满一块的情况下,直接暴力运算即可。
使用条件
可以快速算出一个操作对单独的数的影响。
块与块
一般情况下,n,m
当操作简单或者数据水时,可以到500000
时间复杂度分析
当块长为
对于区间修改,在块内的修改,显然最多把n个数全都改了,时间复杂度O(q)
在单块的暴力修改,最多修改两个块长-2,时间复杂度2*n/q-2=
∴时间复杂度为O(n/q+q);由均值不等式知,当q=
附加:对于基础区间查询
区间查询:类似区间加法,暴力统计左右不完整块的答案,然后统计完整块。时间复杂度:O(
为了方便,我们都默认下文的分块大小为
例题(更新中)
例1:分块1
分块模板题,单点修改,区间查询
既然是模板题,非常好想,只要仔细阅读本文前面就可以轻松AC
注意!!!!开快读,因为原来是树状数组的题,所以卡常!
附上代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,len;
int v[50005],b[50005],tag[50005];
void add(int l,int r,int d) {
for(int i=l;i<=min(b[l]*len,r);i++)v[i]+=d;
if(b[l]!=b[r]) {
for(int i=(b[r]-1)*len+1;i<=r;i++)v[i]+=d;
}
for(int i=b[l]+1;i<=b[r]-1;i++)tag[i]+=d;
}
int main() {
cin>>n;len=sqrt(n);
for(int i=1;i<=n;i++)cin>>v[i];
for(int i=1;i<=n;i++)b[i]=(i-1)/len+1;
for(int i=1;i<=n;i++){
int c,l,r,d;
cin>>c>>l>>r>>d;
if(c==0)add(l,r,d);
if(c==1)cout<<v[r]+tag[b[r]]<<'\n';
}
return 0;
}
例2:分块2
这道题就是区间修改+区间查询。
不需要开快读。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,len,b[120005],m;
ll v[120005],tag[120005],sum[120005];
void add(int l,int r,ll d){
for(int i=l;i<=min(b[l]*len,r);i++){
v[i]+=d;sum[b[l]]+=d;
}
if(b[l]!=b[r])
for(int i=(b[r]-1)*len+1;i<=r;i++)
v[i]+=d,sum[b[r]]+=d;
for(int i=b[l]+1;i<=b[r]-1;i++)tag[i]+=d;
}
ll ask(int l,int r){
ll ans=0;
for(int i=l;i<=min(b[l]*len,r);i++)ans+=v[i]+tag[b[l]];
if(b[l]!=b[r])
for(int i=(b[r]-1)*len+1;i<=r;i++)
ans+=v[i]+tag[b[r]];
for(int i=b[l]+1;i<=b[r]-1;i++)ans+=sum[i]+len*tag[i];
return ans;
}
int main(){
n=read();m=read();len=sqrt(n);
for(int i=1;i<=n;++i)v[i]=read();
for(int i=1;i<=n;++i){
b[i]=(i-1)/len+1;sum[b[i]]+=v[i];
}
for(int i=1;i<=m;++i){
int c=read(),l=read(),r=read();
if(c==1){
int d=read();add(l,r,d);
}
if(c==2)printf("%lld\n",ask(l,r));
}
return 0;
}
例3:分块3
给出一个长
时间复杂度证明:
设块长为
预处理排序,复杂度
对于区间修改操作,仍然是维护加法tag,暴力修改两边的块+重新排序,复杂度
对于区间查询操作,最坏在
总复杂度
一般取q=
但是,如果将q取稍小些,复杂度会更优,由于证明较为复杂,不再提及。
upd:经过测试,当q的值大约为
不知道你们的电脑是怎么样的反正我的和LOJ的评测机都是这样
不过此题卡常,建议快读。
代码实现:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,len,m;
int v[500005],b[500005],tag[500005];
vector<int> g[505];
inline void reset(int x){
g[x].clear();
for(int i=(x-1)*len+1;i<=min(x*len,n);++i)g[x].push_back(v[i]);
sort(g[x].begin(),g[x].end());
}
inline void add(int l,int r,int d) {
for(register int i=l;i<=min(b[l]*len,r);++i)v[i]+=d;
reset(b[l]);
if(b[l]!=b[r]) {
for(register int i=(b[r]-1)*len+1;i<=r;++i)v[i]+=d;
reset(b[r]);
}
for(register int i=b[l]+1;i<=b[r]-1;++i)tag[i]+=d;
}
inline int ask(int l,int r,int d){
int ans=0;
for(register int i=l;i<=min(b[l]*len,r);++i){
if(v[i]+tag[b[l]]<d)++ans;
}
if(b[l]!=b[r]){
for(register int i=(b[r]-1)*len+1;i<=r;++i)
if(v[i]+tag[b[r]]<d)ans++;
}
for(register int i=b[l]+1;i<=b[r]-1;++i){
int x=d-tag[i];
ans+=lower_bound(g[i].begin(),g[i].end(),x)-g[i].begin();
}
return ans;
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);
n=read();len=sqrt(n);
for(register int i=1;i<=n;++i)v[i]=read();
for(register int i=1;i<=n;++i){
b[i]=(i-1)/len+1;
g[b[i]].push_back(v[i]);
}
for(register int i=1;i<=b[n];++i)
sort(g[i].begin(),g[i].end());
for(register int i=1;i<=n;++i){
int c=read(),l=read(),r=read(),d=read();
if(c==0){
add(l,r,d);
}
if(c==1)cout<<ask(l,r,d*d)<<'\n';
}
return 0;
}
正在更新中……
本文来自博客园,作者:JZX102624,转载请注明原文链接:https://www.cnblogs.com/JZX102624/p/blocks.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】