C113 带修莫队 P1903 [国家集训队] 数颜色/维护队列
视频链接:
// 带修莫队 O(n^(5/3)) #include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N=1000005; int n,m,B,mq,mr,a[N]; int sum,cnt[N],ans[N]; struct Q{ //询问 int l,r,id,tim; //按l/B、r/B和tim排序 bool operator<(Q &b){ if(l/B!=b.l/B)return l<b.l; if(r/B!=b.r/B)return r<b.r; return tim<b.tim; } }q[N]; struct R{ //替换 int p,c; }R[N]; void add(int x){ if(!cnt[x])sum++; //x第一次则累计 cnt[x]++; //x出现次数 } void del(int x){ cnt[x]--; if(!cnt[x]) sum--; } int main(){ scanf("%d%d",&n,&m); B=pow(n,0.66); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++){ //操作 char c[2]; int l,r; scanf("%s%d%d",c,&l,&r); if(c[0]=='Q')q[++mq]={l,r,mq,mr}; else R[++mr]={l,r}; } sort(q+1,q+1+mq); for(int i=1,l=1,r=0,x=0;i<=mq;i++){ while(l>q[i].l)add(a[--l]); //左扩展 while(r<q[i].r)add(a[++r]); //右扩展 while(l<q[i].l)del(a[l++]); //左删除 while(r>q[i].r)del(a[r--]); //右删除 while(x<q[i].tim){ //时间戳变大,替换 int p=R[++x].p; //位置p介于[l,r],先删旧数,后加新数 if(l<=p&&p<=r)del(a[p]),add(R[x].c); swap(a[p],R[x].c); //交换a,R的对应数 } while(x>q[i].tim){ //时间戳变小,还原 int p=R[x].p; if(l<=p&&p<=r)del(a[p]),add(R[x].c); swap(a[p],R[x--].c); } ans[q[i].id]=sum; } for(int i=1;i<=mq;i++)printf("%d\n",ans[i]); }