[bzoj2120] [洛谷P1903] 数颜色
Description###
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?
Input###
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
Output###
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
Sample Input###
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output###
4
4
3
4
HINT###
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
想法##
带修改莫队。
与普通莫队其实一个意思,就是多了一个tim表示在这个询问操作时已进行了几次修改。
进行一些修改操作使当前状态与这个询问时原本的状态相同。
代码##
注意细节!!!
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 10005;
int block;
int bl(int x) { return (x-1)/block+1; }
int cnt1,cnt2;
struct ask{
int l,r,id,tim;
bool operator < (const ask &b) const{
return bl(l)<bl(b.l) || (bl(l)==bl(b.l) && r<b.r);
}
}q[N];
struct cg{
int x,fr,to;
}d[N];
int a[N],cnt[1000005];
int cur,L,R;
void add(int x){
cnt[a[x]]++;
if(cnt[a[x]]==1) cur++;
}
void del(int x){
cnt[a[x]]--;
if(cnt[a[x]]==0) cur--;
}
void cg_add(int x){
int id=d[x].x;
if(L<=id && id<=R){ // 注意是id(即d[x].x)啊,并不是x
del(id);
a[id]=d[x].to;
add(id);
}
else a[id]=d[x].to;
}
void cg_del(int x){
int id=d[x].x;
if(L<=id && id<=R){
del(id);
a[id]=d[x].fr;
add(id);
}
else a[id]=d[x].fr;
}
int n,m;
int ans[N];
int main()
{
char opt[1];
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
scanf("%s",opt);
scanf("%d%d",&x,&y);
if(opt[0]=='Q'){
cnt1++;
q[cnt1]=(ask){x,y,cnt1,cnt2};
}
else{
cnt2++;
d[cnt2]=(cg){x,a[x],y};
a[x]=y;
}
}
block=sqrt(n);
sort(q+1,q+cnt1+1);
int t=cnt2;
L=R=1;
cnt[a[1]]++; cur=1;
for(int i=1;i<=cnt1;i++){
while(R<q[i].r) add(++R);
while(L>q[i].l) add(--L);
while(R>q[i].r) del(R--);
while(L<q[i].l) del(L++);
while(t>q[i].tim) cg_del(t--); //注意顺序!!
while(t<q[i].tim) cg_add(++t);
ans[q[i].id]=cur;
}
for(int i=1;i<=cnt1;i++) printf("%d\n",ans[i]);
return 0;
}
既然选择了远方,便只顾风雨兼程