题目描述
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
2、 R P Col 把第P支画笔替换为颜色Col。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入输出格式
输入格式:
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。
第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。
第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式:
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
输入输出样例
输入样例#1:
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
输出样例#1:
4
4
3
4
说明
对于100%的数据,N≤50000,M≤50000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
本题可能轻微卡常数
题解
-
带修改莫队裸题
-
学过莫队的人都知道,普通的莫队是不带修改操作的,但是后人在普通莫队的基础上,又加以改进,发明了带修改莫队,它的思想很简单易懂
-
只有当出现查询操作时,前面的修改操作才有用,也就是说查询和修改两个操作是彼此独立的,因此,我们可以分别记录一下,哪些是查询,哪些是修改,然后普通莫队就行
-
这样就够了吗?
-
当然不行,我们还要记录一下到当前为止我们已经修改了几次,如果我们修改次数大于查询的序号,即改多了,我们就改回去,改少了就改过去
-
举个例子,比如当前修改了3次,而最近的查询操作在第五次,我们就还需要进行第4,5次修改操作
-
如果之前学过普通莫队的看这份代码应该没什么问题
-
洛谷数据加强了,开(sqr(n))会T飞,开(7*sqr(n))快很多
Code
#include<bits/stdc++.h>
#define in(i) (i=read())
using namespace std;
int read() {
int ans=0,f=1; char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0'; i=getchar();}
return ans*f;
}
int n,m,qnum,cnum,tot;
int c[1000010],cnt[1000010],ans[1000010],where[1000010];
struct change {
int pos,col;
}a[1000010];
struct query {
int l,r,id,pre;
}e[1000010];
bool cmp(query a,query b) {
if(where[a.l]!=where[b.l]) return where[a.l]<where[b.l];
if(where[a.r]!=where[b.r]) return where[a.r]<where[b.r];
return a.pre<b.pre;
}
void add(int x) {
cnt[c[x]]++;
if(cnt[c[x]]==1) tot++;
}
void remove(int x) {
cnt[c[x]]--;
if(!cnt[c[x]]) tot--;
}
void work(int now,int i) {
if(a[now].pos>=e[i].l && a[now].pos<=e[i].r) {
if(--cnt[c[a[now].pos]]==0) tot--;
if(++cnt[a[now].col]==1) tot++;
}
swap(a[now].col,c[a[now].pos]);
}
int main()
{
in(n); in(m);
int block=sqrt(n)*7;
for(int i=1;i<=n;i++) {in(c[i]); where[i]=(i-1)/block+1;}
for(int i=1;i<=m;i++) {
char op; cin>>op;
if(op=='Q') {
in(e[++qnum].l); in(e[qnum].r);
e[qnum].id=qnum;
e[qnum].pre=cnum;
}
else {
in(a[++cnum].pos); in(a[cnum].col);
}
}
sort(e+1,e+1+qnum,cmp);
for(int i=1,curl=1,curr=0,now=0;i<=qnum;i++) {
int l=e[i].l, r=e[i].r;
while(curl<l) remove(curl++);
while(curl>l) add(--curl);
while(curr<r) add(++curr);
while(curr>r) remove(curr--);
while(now<e[i].pre) work(++now,i);
while(now>e[i].pre) work(now--,i);
ans[e[i].id]=tot;
}
for(int i=1;i<=qnum;i++) printf("%d
",ans[i]);
return 0;
}
边界问题
最开始now=0,所以先增再执行
最后此时now不符合条件,所以先执行再减