顾名思义,带有修改的莫队。
莫队是一个离线算法,如果用强制在线的问题就不用考虑莫队了(可以树树树?)。
如果用莫队算法求解,必须离线,先把查询操作和修改操作分别记录下来。记录查询操作的时候,增加一个变量,记录本次查询前做了多少次修改。
加上时间轴的带修改莫队
如果没有修改,就是基础莫队,一个查询的左右端点是
[
L
,
R
]
[L, R]
[L,R]。加上修改之后,一个查询表示为
(
L
,
R
,
t
)
(L, R, t)
(L,R,t),
t
t
t表示在查询
[
L
,
R
]
[L, R]
[L,R]前进行了
t
t
t次修改操作。可以把t理解为“时间”,
t
t
t的范围是
1
≤
t
≤
m
1 ≤ t ≤ m
1≤t≤m,
m
m
m是操作次数。
从一个查询移动到另一个查询,除了
L
、
R
L、R
L、R发生变化外,还要考虑
t
t
t的变化。如果两个查询的
t
t
t相同,说明它们是基于同样的数列;如果
t
t
t不同,两个查询所对应的数列是不同的,那么就需要补上这变化(类似于莫队
L
,
R
L,R
L,R修改,只是根据操作次数考虑怎么修改操作的影响,可以想象成离线下来后用时间轴前推后推)。两个查询的
t
t
t相差越小,它们对应的数列差别越小,计算量也越小,所以对
t
t
t排序能减少计算量。
算法复杂度几何解释(类似普通莫队)
与基础莫队一样,也可以给出带修改莫队的几何解释。基础莫队的左右端点
[
L
,
R
]
[L, R]
[L,R],对应平面上的点
(
L
,
R
)
( L,R)
(L,R) ,带修改的莫队
(
L
,
R
,
t
)
(L, R, t)
(L,R,t)对应立体空间的
(
L
,
R
,
t
)
(L,R,t )
(L,R,t)。每个查询对应立体空间的一个点,那么从一个查询到另一个查询,就是从一个点到另一个点。计算复杂度仍然是两点之间的曼哈顿距离。
带修莫队排序步骤
模仿基础莫队的分块思路。定义带修改莫队的排序,按以下步骤执行:
(
1
)
(1)
(1)按左端点
L
L
L排序。若左端点
L
L
L在同一个块,执行
(
2
)
(2)
(2)。
L
L
L对应
x
x
x轴。
(
2
)
(2)
(2)按右端点
R
R
R排序。若右端点
R
R
R在同一个块,执行
(
3
)
(3)
(3)。
R
R
R对应
y
y
y轴。
(
3
)
(3)
(3)按时间
t
t
t排序。
t
t
t对应
z
z
z轴。
左端点
L
L
L所在的块是第
1
1
1查询关键字,右端点
R
R
R所在的块是第
2
2
2关键字,时间
t
t
t是第
3
3
3关键字。
块的大小
具体请看这篇
取block=
n
2
3
n^{\frac {2}{3}}
n32
注意:针对带修改莫队的排序步骤,因为 t t t是第三关键字,所以奇偶性排序都是针对 t t t操作的。修改分为两部分:1.若修改的位置在当前区间内,需要更新答案(del原颜色,add修改后的颜色)。2.无论修改的位置是否在当前区间内,都要进行修改(以供add和del函数在以后更新答案)。
一道例题:数颜色
不卡常了,算了
#include <bits/stdc++.h>
using namespace std;
const int N=(int)1e6+50;
int n,m,a[N],block,ans[N],pos[N];
int cnt1,cnt2,L=1,R=0,now=0,Ans,cnt[N];
char c[10];
struct change{int P,col;}p[N];
struct node{
int L,R,t,id;
}q[N];
inline void del(int x){
--cnt[x];
if(cnt[x]==0) --Ans;
}
inline void add(int x){
++cnt[x];
if(cnt[x]==1) ++Ans;
}
inline void modify(int x,int ti){
if(p[ti].P>=q[x].L&&p[ti].P<=q[x].R){
del(a[p[ti].P]);
add(p[ti].col);
}
swap(a[p[ti].P],p[ti].col);
}
inline bool cmp(node a,node b){
if(pos[a.L]!=pos[b.L]) return a.L<b.L;
else if(a.R!=b.R) return a.R<b.R;
else if(a.R==b.R) return a.t>b.t;
else return a.t<b.t;
}
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c^48);c=getchar();}
return cnt*f;
}
signed main(){
ios::sync_with_stdio(false);
n=read(),m=read();block=pow(n,0.666666);
for(int i=1;i<=n;++i){a[i]=read();pos[i]=(i-1)/block+1;}
for(int i=1;i<=m;++i){
scanf("%s",c+1);//cnt2代表当前修改数 t代表在这次询问之前修改了多少次
if(c[1]=='Q'){q[++cnt1].L=read(),q[cnt1].R=read();q[cnt1].t=cnt2;q[cnt1].id=cnt1;}
if(c[1]=='R'){p[++cnt2].P=read(),p[cnt2].col=read();}
}
sort(q+1,q+1+cnt1,cmp);
for(int i=1;i<=cnt1;++i){
while(L>q[i].L){
--L;
++cnt[a[L]];
if(cnt[a[L]]==1) ++Ans;
}// add(a[--L]);
while(L<q[i].L) {
--cnt[a[L]];
if(cnt[a[L]]==0) --Ans;
++L;
}// del(a[L++]);
while(R>q[i].R) {
--cnt[a[R]];
if(cnt[a[R]]==0) --Ans;
--R;
}// del(a[R--]);
while(R<q[i].R) {
++R;
++cnt[a[R]];
if(cnt[a[R]]==1) ++Ans;
}// add(a[++R]);
while(now<q[i].t) modify(i,++now);
while(now>q[i].t) modify(i,now--);
ans[q[i].id]=Ans;
}
for(int i=1;i<=cnt1;++i){cout<<ans[i]<<endl;}
return 0;
}