C113 带修莫队 P1903 [国家集训队] 数颜色/维护队列

视频链接:

 

 

 

Luogu 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]);
}

 

posted @ 2024-04-06 13:33  董晓  阅读(237)  评论(1编辑  收藏  举报