墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
2、 R P Col 把第P支画笔替换为颜色Col。
题解:
做这道题之前不会带修改莫队,山神学会了之后手(嘴)把手(嘴)教我打代码。
莫队算法先放下,带修改莫队是酱的:记下所有的修改,同时为每个询问加入一个时间标记,表示这个询问发生时修改到了哪一步。求解之前记录一个时间戳。每次求解一个询问时,先按普通莫队求解,然后查看当前时间戳和这个询问的标记,不一样的话就一个时间一个时间地改过去,同时修改答案。进行时间修改时,比如这道题,swap了修改的颜色和修改前的颜色,这样可以让之后发生时间倒流。
代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #define MXN 10000+1 #define MXC 1000000+1 struct Query{int l,r,t,id;}q[MXN]; struct change{int p,co;}c[MXN]; int block[MXN]; bool cmp(Query A,Query B){ if(block[A.l]==block[B.l]){ if(A.r==B.r) return A.t<B.t; else return A.r<B.r; } else return block[A.l]<block[B.l]; } int n,m,x,y; int qsum,csum; std::string p; int color[MXN]; int man[MXC]; int ans[MXN]; void Change(int T,int i,int &temp){ if(c[T].p>=q[i].l&&c[T].p<=q[i].r){ man[color[c[T].p]]--; if(man[color[c[T].p]]==0) temp--; man[c[T].co]++; if(man[c[T].co]==1) temp++; } std::swap(color[c[T].p],c[T].co); return; } int main(){ scanf("%d%d",&n,&m); int s=sqrt(n); for(int i=1;i<=n;i++){ scanf("%d",&color[i]); block[i]=i/s; } while(m--){ std::cin>>p; scanf("%d%d",&x,&y); if(p[0]==‘Q‘){ q[qsum].l=x; q[qsum].r=y; q[qsum].id=qsum; q[qsum++].t=csum; } if(p[0]==‘R‘){ c[++csum].p=x; c[csum].co=y; } } std::sort(q,q+qsum,cmp); int L=0,R=0,T=0,temp=0; for(int i=0;i<qsum;i++){ while(L<q[i].l){man[color[L]]--;if(man[color[L]]==0) temp--;L++;} while(L>q[i].l){L--;man[color[L]]++;if(man[color[L]]==1) temp++;} while(R<q[i].r){R++;man[color[R]]++;if(man[color[R]]==1) temp++;} while(R>q[i].r){man[color[R]]--;if(man[color[R]]==0) temp--;R--;} while(T<q[i].t){T++;Change(T,i,temp);} while(T>q[i].t){Change(T,i,temp);T--;} ans[q[i].id]=temp; } for(int i=0;i<qsum;i++) printf("%d\n",ans[i]); return 0; }
PS:压行严重。