前置知识:普通莫队
可以看我的博客莫队基础(Mo‘s algorithm)
带修莫队的核心思想
带修莫队(Mo’s Algorithm with Updates)是普通莫队算法的扩展,用于处理带修改的区间查询问题。其核心思想是在普通莫队二维(左端点 l l l、右端点 r r r)的基础上,引入第三维——时间 t t t,将每个查询与一个“时间戳”关联,从而在离线处理时能够正确地回退或应用修改操作。
带修莫队的基本实现步骤:
1.定块长
本题将块长定为
n
2
÷
3
n^{2\div3}
n2÷3 时间复杂度较
n
\sqrt n
n 优。
核心代码:
len=pow(n,2.0/3.0);
2.排序
就是如果 l l l 在的块是奇数块,那么 r r r 按照从小到大排序,否则按照大从小排序。
struct query{int l,r,t,id;}q[N];//询问
bool cmp(query x,query y)
{
return pos[x.l]!=pos[y.l]?x.l<y.l:(pos[x.r]!=pos[y.r]?x.r<y.r:x.t<y.t);
}
3.移动区间,获得答案
for(int i=1,l=1,r=0,t=0;i<=cnt_q;i++)
{
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(l<q[i].l)del(l++);
while(r>q[i].r)del(r--);
while(t<q[i].t)modify(i,++t);
while(t>q[i].t)modify(i,t--);
ans[q[i].id]=sum;
}
4.带修与普通的区别
modify()函数是带修莫队特有的。
void modify(int x,int t)
{
if(u[t].id>=q[x].l&&u[t].id<=q[x].r)
{
del(u[t].id);
int tmp=a[u[t].id];
a[u[t].id]=u[t].val;
add(u[t].id);
a[u[t].id]=tmp;
}
swap(a[u[t].id],u[t].val);
}
其他与普通莫队没差别。
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5,M=2e3+5;
int n,m,len,num,sum,a[N],L[M],R[M],cnt[N],ans[N],pos[N];
struct query{int l,r,t,id;}q[N];
struct update{int id,val;}u[N];
bool cmp(query x,query y)
{
return pos[x.l]!=pos[y.l]?x.l<y.l:(pos[x.r]!=pos[y.r]?x.r<y.r:x.t<y.t);
}
void init()
{
len=pow(n,2.0/3.0);
num=n/len;
if(n%len!=0)num++;
for(int i=1;i<=num;i++)
{
L[i]=(i-1)*len+1;
R[i]=i*len;
}
R[num]=n;
for(int i=1;i<=num;i++)
{
for(int j=L[i];j<=R[i];j++)
{
pos[j]=i;
}
}
}
void add(int x)
{
cnt[a[x]]++;
if(cnt[a[x]]==1)++sum;
}
void del(int x)
{
cnt[a[x]]--;
if(!cnt[a[x]])--sum;
}
void modify(int x,int t)
{
if(u[t].id>=q[x].l&&u[t].id<=q[x].r)
{
del(u[t].id);
int tmp=a[u[t].id];
a[u[t].id]=u[t].val;
add(u[t].id);
a[u[t].id]=tmp;
}
swap(a[u[t].id],u[t].val);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
init();
char opt;
int cnt_q=0,cnt_r=0;
for(int i=1;i<=m;i++)
{
cin>>opt;
if(opt=='Q')
{
cnt_q++;
cin>>q[cnt_q].l>>q[cnt_q].r;
q[cnt_q].t=cnt_r;
q[cnt_q].id=cnt_q;
}
if(opt=='R')
{
cnt_r++;
cin>>u[cnt_r].id>>u[cnt_r].val;
}
}
sort(q+1,q+cnt_q+1,cmp);
for(int i=1,l=1,r=0,t=0;i<=cnt_q;i++)
{
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(l<q[i].l)del(l++);
while(r>q[i].r)del(r--);
while(t<q[i].t)modify(i,++t);
while(t>q[i].t)modify(i,t--);
ans[q[i].id]=sum;
}
for(int i=1;i<=cnt_q;i++)
{
cout<<ans[i]<<'\n';
}
return 0;
}
与普通莫队的对比
| 维度 | 普通莫队 | 带修莫队 |
|---|---|---|
| 状态空间 | ( l , r ) (l, r) (l,r) 二维 | ( l , r , t ) (l, r, t) (l,r,t) 三维 |
| 排序关键字 | p o s [ l ] pos[l] pos[l], p o s [ r ] pos[r] pos[r] | p o s [ l ] pos[l] pos[l], p o s [ r ] pos[r] pos[r], t t t |
| 指针 | c u r L curL curL, c u r R curR curR | c u r L curL curL, c u r R curR curR, c u r T curT curT |
| 块长 | B = n B = \sqrt{n} B=n | B = n 2 / 3 B = n^{2/3} B=n2/3 |
&spm=1001.2101.3001.5002&articleId=163781166&d=1&t=3&u=443caf9620de4527bd79bcd06e05be47)
220

被折叠的 条评论
为什么被折叠?



