专栏文章
基于多层分块嵌套的快速区间查询+修改算法
休闲·娱乐参与者 55已保存评论 62
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 60 条
- 当前快照
- 1 份
- 快照标识符
- @mjojrra7
- 此快照首次捕获于
- 2025/12/28 01:01 2 个月前
- 此快照最后确认于
- 2026/02/19 01:24 12 小时前
你是否因为分块算法的 区间操作太慢而烦恼?别着急,我们可以多套几层分块解决。
Part 1 双层分块嵌套
首先分析分块算法复杂度瓶颈。
整块查询非常慢,我们不难想到少开几块,可是这样散块可能非常多,怎么办?我们不妨继续使用分块来维护散块,根据数学知识,大块数量取 ,每个大块分的小块数量也取 最快。
此时我们的复杂度是 的。
Part 2 多层分块嵌套
既然已经有了这样的优化思路,我们可以继续优化到 。那岂不是可以一直优化?
其实并不是,我们发现现在常数已经爆炸了。因此如果我们分块 层,那么时间复杂度事实上是 的,这样看起来就合理多了。根据我们的高中知识,当 时取得最小值,此时:。
但是这里有一个致命的问题!当 时每层块数为自然对数 ,因此我们要对块数取整。
这里我取块数为二,容易证明,这样的区间操作复杂度是 。
我们可以采取递归的方式实现这个算法。
Part 3 代码
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100010];
struct block{
int l,r;//块左右端点
int sum,add;//区间和懒标记
}tr[400010];
void pushup(int p){
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
}
void pushdown(int p){
tr[p<<1|0].add+=tr[p].add;
tr[p<<1|1].add+=tr[p].add;
tr[p<<1|0].sum+=tr[p].add*(tr[p<<1|0].r-tr[p<<1|0].l+1);
tr[p<<1|1].sum+=tr[p].add*(tr[p<<1|1].r-tr[p<<1|1].l+1);
tr[p].add=0;
}
void build(int s,int t,int p){
tr[p].l=s,tr[p].r=t;//记录块左右端点
if(s==t){
tr[p].sum=a[s];
return;
}
int mid=s+t>>1;
build(s,mid,p<<1|0);//递归建块
build(mid+1,t,p<<1|1);
pushup(p);
}
int query(int l,int r,int s,int t,int p){
if(l<=s&&t<=r)return tr[p].sum;//整块直接返回
pushdown(p);
int mid=s+t>>1,res=0;
//处理散块,继续套用分块
if(l<=mid)res+=query(l,r,s,mid,p<<1|0);
if(r>mid)res+=query(l,r,mid+1,t,p<<1|1);
return res;
}
void modify(int l,int r,int c,int s,int t,int p){//同上
if(l<=s&&t<=r){
tr[p].add+=c;
tr[p].sum+=c*(t-s+1);
return;
}
pushdown(p);
int mid=s+t>>1;
if(l<=mid)modify(l,r,c,s,mid,p<<1|0);
if(r>mid)modify(l,r,c,mid+1,t,p<<1|1);
pushup(p);
}
main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,n,1);
for(int i=0;i<m;i++){
int op;
cin>>op;
if(op==1){
int x,y,k;
cin>>x>>y>>k;
modify(x,y,k,1,n,1);
}
else {
int x,y;
cin>>x>>y;
cout<<query(x,y,1,n,1)<<endl;
}
}
}
相关推荐
评论
共 62 条评论,欢迎与作者交流。
正在加载评论...