分块和莫队
分块
什么是分块?
分块,就是把一个整体转化为多个离散的小块进行处理.
它的核心思想就是要把每次修改或询问拆成一个个小部分,两边进行暴力处理,中间的整块进行整块操作.
首先,如何分.
块长不宜太大也不宜太小,一般选取大小为 即可.
首先处理出一个 belong 数组,表示每一个位置属于哪一个块. 记录一个 st、ed 表示每一个块内的起始和末尾.
点击查看代码
len=sqrt(n);
for(rll i=1;i<=len;i++)
st[i]=n/len*(i-1)+1,
ed[i]=n/len*i;
ed[len]=n;
for(rll i=1;i<=len;i++)
for(rll j=st[i];j<=ed[i];j++)
bel[j]=i;
如何修改:
假设当前要修改的区间是 [l,r].
若 l 和 r 处于同一块内,直接暴力扫即可.
若不处于同一块内,先把两边的单独部分暴力处理,然后对于整块的部分,直接加上块中的标记.
这样每次处理复杂度是 O(n) 的.
具体实现:
点击查看代码
inline void update(rll l,rll r,rll v)
{
if(bel[l]==bel[r])
{
for(rll i=l;i<=r;i++) a[i]+=v,sum[bel[l]]+=v;
return;
}
for(rll i=l;i<=ed[bel[l]];i++) a[i]+=v,sum[bel[l]]+=v;
for(rll i=st[bel[r]];i<=r;i++) a[i]+=v,sum[bel[r]]+=v;
for(rll i=bel[l]+1;i<bel[r];i++) mark[i]+=v;
}
查询的话,和修改大致是一样的. 也是单块/两头暴力扫,中间整块加.
具体实现:
点击查看代码
inline void query(rll l,rll r)
{
rll ans=0;
if(bel[l]==bel[r])
{
for(rll i=l;i<=r;i++) ans+=a[i]+mark[bel[l]];
return ans;
}
for(rll i=l;i<=ed[bel[l]];i++) ans+=a[i]+mark[bel[l]];
for(rll i=st[bel[r]];i<=r;i++) ans+=a[i]+mark[bel[r]];
for(rll i=bel[l]+1;i<bel[r];i++) ans+=sum[i]+mark[i]*(ed[i]-st[i]+1);
return ans;
}
莫队
也是一个解决区间查询问题的算法.
具体操作:
初始钦定一个区间 [l,r],对于每一次询问,将其拓展到 [l-1,r]、[l+1,r]、[l,r-1]或[l,r+1].
离线把每一个询问存起来,按照奇偶化排序,处理时将 l 和 r 指针移动到每一个询问的两端点.
点击查看代码
sort(q+1,q+m+1);
for(rll i=1,l=1,r=0;i<=m;i++)
{
while(l>q[i].l) upd(a[--l],1);
while(r<q[i].r) upd(a[++r],1);
while(l<q[i].l) upd(a[l++],-1);
while(r>q[i].r) upd(a[r--],-1);
ans[q[i].id]=ANS;
}
注意这个加减的先后.
奇偶化排序,就是按照修改区间 l 所属块 bel[l] 排序,如果相等就以 r 为关键字,如果 l 所在块编号为奇数,那么从小到大排序;否则从大到小排序.
点击查看代码
struct node
{
ll l,r,id;
inline friend bool operator<(rg node a,rg node b)
{
if(bel[a.l]^bel[b.l]) return a.l<b.l;
if(bel[a.l]&1) return a.r<b.r; return a.r>b.r;
}
}q[maxn];
带修莫队
加一个时间戳 t记录已进行几次修改,并记录距离本次查询最近的修改位置.
每次查询的时候,如果查询要求修改的更多,那就把这些(从上次查询到这次查询之间的更新)修改一下.
码先不打了.
回滚莫队
不是 noip 要求掌握的内容,暂时不写.
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/articles/16777973.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!