【学习笔记】分块
upd on 2022.11.13 16:59
分块是一种比较朴素的数据结构。
但我认为它是相对比较灵活的。
核心思想就是对数组进行分块
对于每一块维护相应的信息
常见的分块思想都可以用“大段维护,局部朴素”来形容(出自李煜东老师的《算法竞赛进阶指南》)
预处理:
1.确定块长,这里有一个技巧,也是一个重点,就是可以调整块长
2.确定每一块的区间(左端点和右端点),每个点所在的块编号
修改:
1.在同一块里,暴力处理
2.不在同一块中,把 左端点所在块 和 右端点所在块 单独暴力处理,
中间的各个块,每一块打上tag
查询:
1.在同一块里,暴力查询
2.不在同一块中,把 左端点所在块 和 右端点所在块 单独暴力查询,
中间的各个块,把答案加上每一块块整体的信息
非常朴素,时间复杂度为 \(O((N+Q)\sqrt{N} )\) ,是为数不多的带着根号的做法
在 \(2\times 10^{5}\) 的数据范围内能轻松跑过,
同时又常数小的优点,对于我这种大常数选手来说,完全比线段树好多了!
而且好写,通用,可以处理非区间可加性问题(比如求众数)
所以实在不行,在\(n\)比较大的时候(只能使用\(O(n\log_{2}{n})\)做法),也可以用分块骗暴力分,甚至怒草\(AC\)。
然后上代码。
以洛谷 P3372 【模板】线段树 1为例,做区间修改和区间查询
一、预处理
1.确定块长,想调整也可以调整,因为针对不同数据,不同的块长的效率不一样。块长太长了和暴力没什么区别,太短了也和暴力没什么区别,通常取\(\left \lfloor \sqrt{n} \right \rfloor\)
2.确定每一块的区间(左端点和右端点),每个点所在的块编号
以\(d\)为块长,\(dn\)为块的数量
d=(int)sqrt(n);
if(n%d==0)
dn=n/d;
else
dn=n/d+1;
这里要判断整除的。因为不能整除的话后面会有一小段。
void pre(){
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++){
if((i-1)%d==0)
pos[i]=pos[i-1]+1;
else
pos[i]=pos[i-1];
}
for(int i=1;i<=dn;i++){
L[i]=(i-1)*d+1;
R[i]=min(n,i*d);
s[i]=sum[R[i]]-sum[L[i]-1];
}
return;
}
这里是先对每一块设置基本信息,本题中的信息是区间和,是先用前缀和预处理了一下的。
二、修改:
1.在同一块里,暴力扫过去,每个单点加
2.不在同一块中,把 左端点所在块 和 右端点所在块 单独暴力扫,单点,
中间的各个块,每一块的tag加,tag在本题中的含义是快被整体加了多少
void upd(int l,int r,int v){
int pl=pos[l],pr=pos[r];
if(pl==pr){
for(int i=l;i<=r;i++)
a[i]+=v;
s[pl]+=v*(r-l+1);
return;
}
for(int i=pl+1;i<=pr-1;i++)
tag[i]+=v;
for(int i=l;i<=R[pl];i++)
a[i]+=v;
s[pl]+=v*(R[pl]-l+1);
for(int i=L[pr];i<=r;i++)
a[i]+=v;
s[pr]+=v*(r-L[pr]+1);
return;
}
这里用\(pl\)和\(pr\)代表左右端点所在区块的编号
还是比较好理解的
三、查询:
1.在同一块里,暴力查询
2.不在同一块中,把 左端点所在块 和 右端点所在块 单独暴力查询,
中间的各个块,把答案加上每一块块整体的信息
int query(int l,int r){
int pl=pos[l],pr=pos[r];
int ans=0;
if(pl==pr){
for(int i=l;i<=r;i++)
ans+=a[i];
ans+=tag[pl]*(r-l+1);
return ans;
}
for(int i=pl+1;i<=pr-1;i++)
ans+=s[i]+tag[i]*(R[i]-L[i]+1);
for(int i=l;i<=R[pl];i++)
ans+=a[i];
ans+=tag[pl]*(R[pl]-l+1);
for(int i=L[pr];i<=r;i++)
ans+=a[i];
ans+=tag[pr]*(r-L[pr]+1);
return ans;
}
基本上和修改差不多,应该是不用讲了。
然后就完了。很好写,也很快,就\(AC\)了。
所以分块\(yyds\)!
2022.11.13

浙公网安备 33010602011771号