给出一个大小为n的数组,有q次操作(查询[l,r]区间有多少个不同数或者修改某一个值)。
带修改的莫队。和普通莫队不同的是多了一维时间。因此在做区间修改之间,需要把时间序列修补一下。保存查询的时候需要保存当前修改序列的时间,在查询的时候需要走到当前时间。
另外在排序的时候,第二关键字r不再是奇偶block去排了,如果block相等,则按查询顺序,不相等则直接按r的大小。
block的大小从sqrt(n) 变成n^2/3。
int block_size;
struct query{
int l,r,id;
int block;
int r_block;
int t;
bool operator<(const query &q) const{
if(block != q.block) return l<q.l;
if(r_block == q.r_block) return id < q.id; //l,r都在同一个区间,按查询顺序
return r<q.r; //直接按r排
//return ((q.r_block)&1)? r < q.r : q.r<r; // not work
}
}q[N];
struct Revise{
int pos, c, old_c;
};
int c[N],ans[N],num[N],old[N];
int sum;
void add(int idx){
if(num[idx]++==0)sum++;
}
void del(int idx){
if(--num[idx]==0)sum--;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
block_size = pow(n,2.0/3);
fr(i,1,n+1) {
scanf("%d",&c[i]);
old[i] = c[i];
}
vector<query> q;
vector<Revise> modi;
clr(num);
fr(i,0,m) {
char op;
cin>>op;
if(op=='Q'){
int l,r;
scanf("%d%d",&l,&r);
query qy;
qy.l = l;qy.r = r;
qy.id = q.size();
qy.block = qy.l/block_size;
qy.r_block = qy.r/block_size;
qy.t = modi.size()-1;
q.pb(qy);
} else {
Revise r;
scanf("%d%d",&r.pos,&r.c);
r.old_c = old[r.pos];
modi.pb(r);
old[r.pos] = r.c;
}
}
sort(q.begin(),q.end());
int l = 1, r = 0;
int cur = -1;
sum = 0;
for(int i = 0; i < q.size();++i){
//做区间移动时,先处理修改的操作
while(cur<q[i].t){
cur++;
int pos = modi[cur].pos;
int old_c = modi[cur].old_c;
int new_c = modi[cur].c;
if(pos<=r && pos >=l){
del(old_c);
add(new_c);
}
c[pos] = new_c;
}
while(cur>q[i].t){
int pos = modi[cur].pos;
int old_c = modi[cur].old_c;
int new_c = modi[cur].c;
if(pos<=r && pos >=l){
del(new_c);
add(old_c);
}
c[pos] = old_c;
cur--;
}
if(q[i].l == q[i].r){
ans[q[i].id] = 1;
continue;
}
while(l>q[i].l) add(c[--l]);
while(r<q[i].r) add(c[++r]);
while(l<q[i].l) del(c[l++]);
while(r>q[i].r) del(c[r--]);
ans[q[i].id] = sum;
}
fr(i,0,q.size()){
printf("%d\n",ans[i]);
}
}