带修莫队
随便提几笔吧,时间不太充裕就不写太多了。
就是在区间的基础上再加上一个时间。
来个例题 数颜色|维护队列
直接就是考虑按照 \(l\) 所在块, \(r\) 所在块, \(t\) 的优先级排序,然后可以证明在块长取 \(n^{\frac{2}{3}}\) 时最优,可用最劣情况证明其复杂度。
然后就是稍微注意点细节就好,比如离线化问题的时候记得修改原数组之类的。
那么结合例题看下代码就大概能理解了吧。
code:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
inline int read(){
int s=0,f=1;char ch=getchar();
while(ch<'0'||'9'<ch) {if(ch=='-') f=-1;ch=getchar();}
while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=getchar();}
return s*f;
}
const int N=133333+3;
const int MAXN=1000000+3;
int n,Q,Siz;
int s[N],p[N],tot1,tot2;
struct ques{
int l,r,t,opt;
}q[N];
struct opat{
int p,s,f;
}c[N];
bool cmp(ques x,ques y){
if(x.l/Siz==y.l/Siz){
if(x.r/Siz==y.r/Siz) return x.t<y.t;
return x.r/Siz<y.r/Siz;
}
return x.l/Siz<y.l/Siz;
}
bool cmp2(ques x,ques y){
return x.t<y.t;
}
int Num,cnt[MAXN];
inline void add(int x){
if(!cnt[x]) ++Num;
++cnt[x];
}
inline void sub(int x){
--cnt[x];
if(!cnt[x]) --Num;
}
char opt[5];
int ans[N];
int main(){
n=read();Q=read();Siz=pow(n,(double)2/(double)3);
for(int i=1;i<=n;++i){
s[i]=p[i]=read();
}
for(int t=1;t<=Q;++t){
int a,b;
scanf("%s",opt+1);a=read();b=read();
if(opt[1]=='Q'){
++tot1;q[tot1]={a,b,tot1,tot2};
}else{
++tot2;c[tot2]={a,s[a],b};
s[a]=b;
}
}
sort(q+1,q+1+tot1,cmp);
int l=1,r=0,opt=0;
for(int i=1;i<=tot1;++i){
while(opt<q[i].opt){
++opt;
if(l<=c[opt].p&&c[opt].p<=r){
add(c[opt].f);sub(c[opt].s);
}
p[c[opt].p]=c[opt].f;
}
while(q[i].opt<opt){
if(l<=c[opt].p&&c[opt].p<=r){
add(c[opt].s);sub(c[opt].f);
}
p[c[opt].p]=c[opt].s;
--opt;
}
while(l<q[i].l){
sub(p[l]);
++l;
}
while(q[i].l<l){
--l;
add(p[l]);
}
while(r<q[i].r){
++r;
add(p[r]);
}
while(q[i].r<r){
sub(p[r]);
--r;
}
ans[q[i].t]=Num;
}
for(int i=1;i<=tot1;++i){
printf("%d\n",ans[i]);
}
return 0;
}