分块&莫队
作者:互联网
分块
是一种暴力结构
给定一个序列a,q个询问,求区间[L,R]权值和。
显然树状数组,线段树等结构都可做
没有学习它们的时候我们是如何解决这个问题的?
前缀和
那么再加上更新
如果不借助上述数据结构只能暴力维护
for(int i=l;i<=r;++i)a[i]+=data;
for(int i=l;i<=r;++i)ans+=a[i]
之所以成分块是暴力数据结构正是因为分块就是建立在暴力的基础之上的。
分块的核心思想是大段维护,小段暴力
把长度为n的序列分成块长(block)为sqrt(n)的若干块(cnt=n/block)。每一个快都有一个边界l,r,通过数学归纳法可以得出
for(int i=1;i<=cnt;++i){
p[i].l=(i-1)*block+1;
p[i].r=i*block;
}
但是由于分块题经常卡常,用加法代替相对低效的乘法,通常写成这种形式
for(int i=1;i<=cnt;++i){
p[i].l=p[i-1].r+1;
p[i].r=p[i].l+block-1;
}
块里可以维护很多东西,如果要维护的元素不能实现区间的直接转移(也就是传统数据结构不能解决),比如说a在区间中出现的次数,那么分块就是较好的选择
这里维护块中所有元素的和,以及一个lazytag(借鉴线段树的思想,用到的时候再下放)。
更新操作
int bl=belong[l],br=belong[r];
if(bl==br){//在一个块内暴力
for(int i=l;i<=r;++i){
a[i]+=data;
}
}
else{
for(int i=l;i<=p[bl].r;++i){//不是整块暴力
a[i]+=data;
}
for(int i=p[br].l;i<=r;++i){
a[i]+=data;
}
for(int i=bl+1;i<=br-1;++i){
p[i].add+=data;//懒标记
}
}
查询操作
int bl=belong[l],br=belong[r];
if(bl==br){//在一个块内暴力
for(int i=l;i<=r;++i){
sum+=a[i];
}
return sum;
}
else{
for(int i=l;i=p[bl].r;++i){
if(p[bl].add){
sum+=p[bl].add;
}
sum+=a[i];
}
for(int i=p[br].l;i<=r;++i){
if(p[br].add){
sum+=p[br].add;
}
sum+=a[i];
}
for(int i=bl+1;i<=br-1;++i){
sum+=p[i].data+p[i].add;
}
}
时间复杂度(块数)
分块能解决的问题:
1.RMQ,区间权值和等
2.维护区间众数 例题:蒲公英
3.区间中第k小的元素 对每个块排序,然后二分查找下标 例题:教主的魔法
4.以及各种东西 例题:弹飞绵羊
分块题目和传统数据结构题相似,考虑如何预处理,如何查询,如何更新
(这是句废话)
可以通过数学证明得到最佳块长实际上为pow(n,2.0/3),时间复杂度由原来的O(n*sqrt(n)),变为O(n^5/3)
标签:暴力,分块,int,belong,bl,br,莫队 来源: https://www.cnblogs.com/Chano/p/16265425.html