P1903 带修莫队
[国家集训队] 数颜色 / 维护队列
题目描述
墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
-
\(Q\ L\ R\) 代表询问你从第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。
-
\(R\ P\ Col\) 把第 \(P\) 支画笔替换为颜色 \(Col\)。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入格式
第 \(1\) 行两个整数 \(N\),\(M\),分别代表初始画笔的数量以及墨墨会做的事情的个数。
第 \(2\) 行 \(N\) 个整数,分别代表初始画笔排中第 \(i\) 支画笔的颜色。
第 \(3\) 行到第 \(2+M\) 行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式
对于每一个 Query 的询问,你需要在对应的行中给出一个数字,代表第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。
样例 #1
样例输入 #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
提示
对于30%的数据,\(n,m \leq 10000\)
对于60%的数据,\(n,m \leq 50000\)
对于所有数据,\(n,m \leq 133333\)
所有的输入数据中出现的所有整数均大于等于 \(1\) 且不超过 \(10^6\)。
增加了一维时间维
我们 q1 q2 分别保存 查询 修改
我们用cntq cntr 记录查询 修改
那么 q1[cntq].t=cntr q1[cntq].id=cntq
我们离线后处理 1~cntq时
L=1 R=0 T=0
L R与普通莫队一样
关于时间维度 类比 L R while T<q1[i].t T++ upd(i,T) while T>q1[i].t upd(i,T) T--
关于upd操作
inline void upd(int x,int t)
{
if(q1[x].l<=q2[t].l&&q2[t].l<=q1[x].r)//如果修改的点(q2[t].l)在当前查询的区间内 就修改
{
Delete(a[q2[t].l]);
Add(q2[t].r);
//.l存的修改点 .r存的修改值
}
swap(a[q2[t].l],q2[t].r);
}
我们q2[].l 记录的修改点 q2[].r 记录的修改值
最后swap是因为 我们双指针有Add Delete 而对于点修改 Delete等价于还原数据 所以 swap即可
关于 cmp
bool cmp(const md& x,const md& y)
{
if(x.l/len!=y.l/len)return x.l/len<y.l/len;
if(x.r/len!=y.r/len)return x.r/len<y.r/len;
return x.t<y.t;
}
我们按照 l r 分别分了块 所以分别按块序数排序 最后块内按照时间升序排列
注意! sort(q1+1,q1+cntq+1,cmp) 不是 q1+m+1!!!
我们离线操作的始终是查询区间!!!
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,len,a[N],cnt[N],Ans=0,ans[N],cntq,cntc;
struct md {
int l,r,t,id;
} q1[N],q2[N]; //q1:询问 q2:修改
inline void Add(int x) {
cnt[x]++;
Ans+=(cnt[x]==1);
}
inline void Delete(int x) {
cnt[x]--;
Ans-=(cnt[x]==0);
}
inline void upd(int x,int t) {
if(q1[x].l<=q2[t].l&&q2[t].l<=q1[x].r) { //如果修改的点(q2[t].l)在当前查询的区间内 就修改
Delete(a[q2[t].l]);
Add(q2[t].r);
//.l存的修改点 .r存的修改值
}
swap(a[q2[t].l],q2[t].r);
}
bool cmp(const md& x,const md& y) {
if(x.l/len!=y.l/len)return x.l/len<y.l/len;
if(x.r/len!=y.r/len)return x.r/len<y.r/len;
return x.t<y.t;
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m;
len=pow(n,0.66667);//记住就行
for(int i=1; i<=n; i++)cin>>a[i];
for(int i=1; i<=m; i++) {
char op;
int l,r;
cin>>op>>l>>r;
if(op=='Q')cntq++,q1[cntq].l=l,q1[cntq].r=r,q1[cntq].t=cntc,q1[cntq].id=cntq;//t代表第几次修改
else cntc++,q2[cntc].l=l,q2[cntc].r=r;
}
sort(q1+1,q1+cntq+1,cmp);
int L=1,R=0,T=0;
for(int i=1; i<=cntq; i++) {
while(L>q1[i].l)L--,Add(a[L]);
while(R<q1[i].r)R++,Add(a[R]);
while(L<q1[i].l)Delete(a[L]),L++;
while(R>q1[i].r)Delete(a[R]),R--;
while(T<q1[i].t)T++,upd(i,T);//对当前查询区间有影响 修改
while(T>q1[i].t)upd(i,T),T--;//对当前查询区间无影响 撤销修改操作
ans[q1[i].id]=Ans;
}
for(int i=1; i<=cntq; i++)cout<<ans[i]<<"\n";
return 0;
}