P1903 【模板】分块/带修改莫队(数颜色)

hhz6830975

2018-01-08 20:03:01

Personal

莫队算法是一种优化的暴力 适用于已知[l,r],可以快速推出[l-1,r],[l+1,r],[l,r-1],[l,r+1]的题目(一般是O(1)) 它通过调整询问操作的顺序来降低复杂度 以l为第一关键字,l在同一分块时以r为第二关键字进行排序 这样时间复杂度是O((n+m)\*sqrt(n)) 涉及修改时,再以时间为第三关键字 对每个查询记录之前最近的修改编号,操作时记录当前已修改多少个,然后相对该查询之前的修改个数,少了就修改,多了就恢复 ```cpp #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int maxn=10010; int n,m,block,a[maxn]; int hash[maxn],hashN; int ans[maxn],cnt[100010]; int Qnum,Rnum; struct query{ int l,r,t,pre; }Q[maxn]; struct change{ int pos,x; }R[maxn]; bool cmp(query a,query b){ if(a.l/block!=b.l/block) return a.l<b.l; else if(a.r/block!=b.r/block) return a.r<b.r; return a.t<b.t; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; block=sqrt(n); for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=m;i++){ char op; cin>>op; if(op=='Q') Q[++Qnum].t=Qnum,cin>>Q[Qnum].l>>Q[Qnum].r,Q[Qnum].pre=Rnum; else ++Rnum,cin>>R[Rnum].pos>>R[Rnum].x; } sort(Q+1,Q+Qnum+1,cmp); int l=1,r=1,tim=0; ans[0]=1,++cnt[a[1]]; for(int i=1;i<=Qnum;i++){ ans[Q[i].t]=ans[Q[i-1].t]; if(l<Q[i].l) while(l!=Q[i].l){ if(--cnt[a[l++]]==0) --ans[Q[i].t]; } else while(l!=Q[i].l){ if(++cnt[a[--l]]==1) ++ans[Q[i].t]; } if(r<Q[i].r) while(r!=Q[i].r){ if(++cnt[a[++r]]==1) ++ans[Q[i].t]; } else while(r!=Q[i].r){ if(--cnt[a[r--]]==0) --ans[Q[i].t]; } if(tim<Q[i].pre) while(tim!=Q[i].pre){ ++tim; if(R[tim].pos>=l&&R[tim].pos<=r){ if(--cnt[a[R[tim].pos]]==0) --ans[Q[i].t]; if(++cnt[R[tim].x]==1) ++ans[Q[i].t]; } swap(a[R[tim].pos],R[tim].x); //这里学习了一个技巧: //修改的时候直接swap //这样原来的值就存在修改命令里 //下次使用到一定是恢复,再swap即可 } else while(tim!=Q[i].pre){ if(R[tim].pos>=l&&R[tim].pos<=r){ if(--cnt[a[R[tim].pos]]==0) --ans[Q[i].t]; if(++cnt[R[tim].x]==1) ++ans[Q[i].t]; } swap(a[R[tim].pos],R[tim].x); --tim; } } for(int i=1;i<=Qnum;i++) cout<<ans[i]<<endl; return 0; } ```