传送门
题解:在普通莫队的基础上再维护一下时间,每次操作将当前时间之后的修改复原,将当前时间之前的修改完成后,再双指针移动即可。
听说块大小为n^(2/3)更快,有待考证(^_^)。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+2;
int n,m;
int a[MAXN],last[MAXN],cnt[1000002],ans[MAXN],bel[MAXN],tot1=0,tot2=0,siz,ret=0;
bool vis[MAXN];
char opt[10];
struct Query {
int l,r,id,lst;
friend bool operator <(const Query &a,const Query &b) {
if (bel[a.l]^bel[b.l]) return a.l<b.l;
if (a.r^b.r) return a.r<b.r;
return a.lst<b.lst;
}
}q[MAXN];
struct Modify {
int pos,cur,pre;
}w[MAXN];
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
inline void update(int pos) {
if (vis[pos]) {
--cnt[a[pos]];
if (!cnt[a[pos]]) --ret;
}
else {
++cnt[a[pos]];
if (cnt[a[pos]]==1) ++ret;
}
vis[pos]^=1;//Don't forget this!!!
}
inline void change(int pos,int val) {
if (vis[pos]) {
update(pos);
a[pos]=val;
update(pos);
}
else a[pos]=val;
}
int main() {
// freopen("bzoj 2120.in","r",stdin);
memset(vis,false,sizeof(vis));
memset(cnt,0,sizeof(cnt));
n=read(),m=read();
// siz=(int)sqrt(double(n));
siz=465;//n^(2/3)
for (int i=1;i<=n;++i) last[i]=a[i]=read(),bel[i]=i/siz;
for (int i=1;i<=m;++i) {
scanf("%s",opt);
if (opt[0]=='Q') {
q[++tot1].l=read(),q[tot1].r=read();
q[tot1].id=tot1,q[tot1].lst=tot2;
}
else {
w[++tot2].pos=read();
w[tot2].pre=last[w[tot2].pos],w[tot2].cur=read();
last[w[tot2].pos]=w[tot2].cur;
}
}
sort(q+1,q+tot1+1);
for (int i=1,l=1,r=0;i<=tot1;++i) {
for (int j=q[i-1].lst+1;j<=q[i].lst;++j) change(w[j].pos,w[j].cur);
for (int j=q[i-1].lst;j>q[i].lst;--j) change(w[j].pos,w[j].pre);
while (l<q[i].l) update(l++);
while (r>q[i].r) update(r--);
while (l>q[i].l) update(--l);
while (r<q[i].r) update(++r);
ans[q[i].id]=ret;
}
for (int i=1;i<=tot1;++i) printf("%d\n",ans[i]);
return 0;
}