Dynamic len(set(a[L:R]))
题意:给出下标为 0 到 n - 1 的n个数,(后面处理为 1 到 n 的)有两种操作
1.询问从 x 到 y 这个区间内有多少个不一样的数
2.把下标为 x 的数的值改为 y
思路:区间询问,离线查询,可以用莫队,但是是带修改操作的莫队,带修莫队的分块大小为N^(2/3)N23N23
在输入操作的时候,每一个步骤都要记录时间戳,因为有些修改是在查询之前,有些修改是在查询之后的,所以每个操作都要有一个tm标签,记录它的时间,然后对于查询操作,要用一个id记录询问顺序。
然后对询问进行排序,按照分块排序。
然后从前往后进行询问操作,一遍移动时间戳,把该询问时间戳前面的修改都做了,若时间戳在该询问之后,那么就把修改改回去,所以修改操作还要记录修改前的那个值,修改做完了就去移动 l r 标签到对于区间,然后记录答案
带修莫队中最重要的与非带修的不同的地方是 push() 函数,它必须是可以双向操作的,其他的add() 和 dele() 跟一般的是差不多的,还有一个核心就是得记录所有操作的时间戳。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 50004
#define maxm 1000005
struct node1{
int l,r,id,tm;
}ask[maxn];
struct node2{
int last,x,val;
}chg[maxn];
int n,m,tot,cnt,Ans,pos[maxn],last[maxn],a[maxn];
int num[maxm],ans[maxm],flag[maxm];
bool cmp(node1 a,node1 b){ //按照分块对询问排序
if(pos[a.l] != pos[b.l])
return pos[a.l] < pos[b.l];
if(pos[a.r] != pos[b.r])
return pos[a.r] < pos[b.r];
return a.tm < b.tm;
}
void dele(int x){
if(!(--num[a[x]]))//如果去掉该点的值后,该值没了,计数减
Ans--;
flag[x] = 0; //标记该点不在记录中
}
void add(int x){
if(!num[a[x]]) //如果该点的值没有出现过,计数加
Ans++;
num[a[x]]++;
flag[x] = 1; //标记该点已在记录中
}
void push(int x){ //如果这个点原本有,那就删掉,没有就加上
if(flag[x]) //这个操作是因为后面有反向操作
dele(x);
else
add(x);
}
void modify(int x,int v){
if(flag[x]){ // 改变这个点的值,如果原本有值,删掉后赋值,没有就直接赋值
dele(x);
a[x] = v;
add(x);
}else{
a[x] = v;
}
}
int main(){
scanf("%d %d",&n,&m);
int s = pow(n,2.0 / 3.0) + 1; // 带修改的莫队,分块为 n ^ (2/3)
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
last[i] = a[i]; //记录该位置最后的值
pos[i] = (i + 1) / s;
}
for(int i = 1;i <= m;i++){
int x,y;
char opt[5];
scanf("%s %d %d",opt,&x,&y);
x++;
if(opt[0] == 'Q'){
ask[++tot].id = tot; //tot记录询问顺序,cnt记录时间顺序,包括修改的
ask[tot].l = x;
ask[tot].r = y;
ask[tot].tm = cnt;
}else{
chg[++cnt].x = x;
chg[cnt].last = last[x]; //由于修改的返回去要修改回去,所以要记录修改前的值
chg[cnt].val = y;
last[x] = y;
}
}
sort(ask + 1,ask + 1 + tot,cmp);
int l = 1,r = 0,tm = 0;
for(int i = 1;i <= tot;i++){
if(tm < ask[i].tm){ // 把这个询问时间前的修改全部做了
for(int j = tm + 1;j <= ask[i].tm;j++)
modify(chg[j].x,chg[j].val);
}else{ //若是询问在当前时间后面,那么就要把修改了的改回去
for(int j = tm;j >= ask[i].tm + 1;j--)
modify(chg[j].x,chg[j].last);
}
while(l < ask[i].l){
push(l++);
}
while(l > ask[i].l){
push(--l);
}
while(r < ask[i].r){
push(++r);
}
while(r > ask[i].r){
push(r--);
}
ans[ask[i].id] = Ans;
tm = ask[i].tm;
}
for(int i = 1;i <= tot;i++)
printf("%d\n",ans[i]);
return 0;
}