带修莫队算法
莫队算法是一种比较暴力的离线区间查询算法,充分利用前期查询的结果,基于分块的思想进行优化,得到比较优秀的时间复杂度
带修莫队增加了单点修改操作,思想大致如下:
首先记录下每一次查询操作的区间、次序、上一次的修改次序
为了充分优化时间复杂度,将询问按左端点所属区间排序,若左端点在同一区间,再按右端点所在区间排序,这样同一个块中的询问大概在O(n)的时间便可以处理,总的时间复杂度大概是O(n√n)。
保留上一次查询的结果,每次将上一次的查询区间移动到当前区间上来,再看一下上一次的修改次数与当先查询的修改次数,如果多,就该回去,如果少,就改过来。这个操作可以用栈维护,也可以每次修改后将修改值和序列上的值交换,若再次经过这个点,便被换了回去。
板子题:
https://www.luogu.org/problemnew/show/P1903
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int MAXN = 10010 ; inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();} return x*f; } int ans,n,m,belong[MAXN],a[MAXN],color[MAXN],out[MAXN]; int bnum,qnum,unum; struct Query{ int x,y,pre,id; } query[MAXN]; struct Update{ int pos,d; } update[1010]; bool cmp(Query a,Query b) { if(belong[a.x]!=belong[b.x]) return belong[a.x]<belong[b.x]; if(belong[a.y]!=belong[b.y]) return belong[a.y]<belong[b.y]; return a.pre<b.pre; } inline void add(int p) { if(++color[p]==1) ans++; } inline void del(int p) { if(--color[p]==0) ans--; } void work(int now,int i) { if(query[i].x<=update[now].pos&&update[now].pos<=query[i].y) { if(--color[a[update[now].pos]]==0) ans--; if(++color[update[now].d]==1) ans++; } swap(update[now].d,a[update[now].pos]); } void MO() { int l=0,r=0,now=0; for(int i=1;i<=qnum;i++) { while(l<query[i].x) del(a[l++]); while(l>query[i].x) add(a[--l]); while(r<query[i].y) add(a[++r]); while(r>query[i].y) del(a[r--]); while(now<query[i].pre) work(++now,i); while(now>query[i].pre) work(now--,i); out[query[i].id]=ans; } for(int i=1;i<=qnum;i++) printf("%d ",out[i]); } int main() { n=read(); m=read(); bnum=sqrt(n); for(register int i=1;i<=n;i++) a[i]=read(),belong[i]=(i-1)%bnum+1; int a,b; char f[2]; for(register int i=1;i<=m;i++) { scanf("%s",f); a=read();b=read(); if(f[0]=='Q'){ query[++qnum].x=a; query[qnum].y=b; query[qnum].pre=unum; query[qnum].id=qnum; } else{ update[++unum].pos=a; update[unum].d=b; } } sort(query+1,query+1+qnum,cmp); MO(); return 0; }