多层分块树
本文介绍一种我自己发明的多层分块实现的树形数据结构。支持
前言
回顾一下分块处理单点修改,区间查询的过程:
将数组分为
单点修改块
区间求和时,把区间拆成整块和散块,散块暴力累加,整块利用预处理的
本人在学习分块的时候,认为区间求和的过程不够优美,于是想出了一种双层分块:
注意到区间求和,本质上是拆成整块
当我沾沾自喜的时候,某个同学说:“你这个东西常数太大了。”然而执迷不悟坚持不懈的我决定不顾常数继续探究,最终得到了一种新的数据结构。名字还没想好,姑且叫它多层分块树吧。
结构
类似上面说的多层分块,递归定义多层分块树
它的根节点
特别地,对于
操作
建树
下面的代码建立了一个多层分块树,下标范围为
当
当
void build(int d,int len){
if(len>2){
int B=sqrt(len+1);
root[d]=cnt,cnt++,build(root[d],len/B),son[d].resize(len/B+1);
for(int i=0;i<=len/B;i++){
son[d][i]=cnt,cnt++,build(son[d][i],(i^(len/B))?B-1:len%B);
}
}
return;
}
单点修改
下面的代码实现了多层分块树的单点修改。
当
当
void update(int d,int len,int x,int c){
if(len>2){
int B=sqrt(len+1);
v[d]+=c,update(root[d],len/B,x/B,c);
update(son[d][x/B],((x/B)^(len/B))?B-1:len%B,x%B,c);
}
else a[x][d]+=c,v[d]+=c;
return;
}
这个东西应该可以做区间修改,像线段树一样用懒标记就行,但是由于作者太懒就让读者自己去探索吧。
区间查询
下面的代码以区间求和为例,实现了多层分块树的区间查询。
当
当
当
int query(int d,int len,int l,int r){
if(!l&&r==len)return v[d];
int sum=0;
if(len>2){
int B=sqrt(len+1),siz;
if((l/B)^(r/B)){
sum =((l/B+1)^(r/B))?query(root[d],len/B,l/B+1,r/B-1):0;
siz=((l/B)^(len/B))?B-1:len%B;
sum+=query(son[d][l/B],siz,l%B,siz);
siz=((r/B)^(len/B))?B-1:len%B;
sum+=query(son[d][r/B],siz,0,r%B);
}
else{
siz=((l/B)^(len/B))?B-1:len%B;
sum=query(son[d][l/B],siz,l%B,r%B);
}
}
else for(int i=l;i<=r;i++)sum+=a[i][d];
return sum;
}
总代码
下面的代码用多层分块树实现了 【模板】树状数组 1,可以通过。
#include<bits/stdc++.h>
using namespace std;
constexpr int N=5e5+10;
int n,m,a[N],op,x,k,l,r;
struct duocengfenkuaishu{
int root[N<<3],v[N<<3],a[4][N<<3],cnt=2;
vector<int> son[N<<3];
void build(int d,int len){
if(len>2){
int B=sqrt(len+1);
root[d]=cnt,cnt++,build(root[d],len/B),son[d].resize(len/B+1);
for(int i=0;i<=len/B;i++){
son[d][i]=cnt,cnt++,build(son[d][i],(i^(len/B))?B-1:len%B);
}
}
return;
}
void update(int d,int len,int x,int c){
if(len>2){
int B=sqrt(len+1);
v[d]+=c,update(root[d],len/B,x/B,c);
update(son[d][x/B],((x/B)^(len/B))?B-1:len%B,x%B,c);
}
else a[x][d]+=c,v[d]+=c;
return;
}
int query(int d,int len,int l,int r){
if(!l&&r==len)return v[d];
int sum=0;
if(len>2){
int B=sqrt(len+1),siz;
if((l/B)^(r/B)){
sum =((l/B+1)^(r/B))?query(root[d],len/B,l/B+1,r/B-1):0;
siz=((l/B)^(len/B))?B-1:len%B;
sum+=query(son[d][l/B],siz,l%B,siz);
siz=((r/B)^(len/B))?B-1:len%B;
sum+=query(son[d][r/B],siz,0,r%B);
}
else{
siz=((l/B)^(len/B))?B-1:len%B;
sum=query(son[d][l/B],siz,l%B,r%B);
}
}
else for(int i=l;i<=r;i++)sum+=a[i][d];
return sum;
}
}t;
int main(){
scanf("%d%d",&n,&m),t.build(1,n-1);
for(int i=0;i<n;i++)scanf("%d",&a[i]),t.update(1,n-1,i,a[i]);
for(int i=0;i<m;i++){
scanf("%d",&op);
if(op==1)scanf("%d%d",&x,&k),x--,t.update(1,n-1,x,k);
else scanf("%d%d",&l,&r),l--,r--,printf("%d\n",t.query(1,n-1,l,r));
}
return 0;
}
复杂度
空间复杂度
空间复杂度满足
时间复杂度
建树
显然和空间复杂度相同,就是