P1903 [国家集训队] 数颜色 / 维护队列
P1903 [国家集训队] 数颜色 / 维护队列
题目描述
墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
-
\(Q\ L\ R\) 代表询问你从第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。
-
\(R\ P\ C\) 把第 \(P\) 支画笔替换为颜色 \(C\)。
为了满足墨墨的要求,你知道你需要干什么了吗?
数据范围
对于所有数据,\(n,m \leq 133333\)
所有的输入数据中出现的所有整数均大于等于 \(1\) 且不超过 \(10^6\)。
Solution:
十分好的莫队,使我大受震撼。
首先感谢大佬的题解带给我很多启发
我们回顾一下以前学的莫队都在干什么:给一个序列,有很多询问,无修改操作,让我们维护一些可以快速转移的东西。
我们会使用莫队来维护左右端点然后动态的加点或者删点来应对询问,但是要是有修改我们貌似就很难做了。
所以我们转换一下思路:
我们将左右端点分别看成 \(x,y\) 轴,那么我们以前干的事就是在一个二维平面上维护一些能快速转移到相邻点的值。
然后我们发现,如果我们将时间作为 \(z\) 轴,由于我们只需单点修改。我们仍旧可以快速的维护在空间上相邻节点间的快速转移。
如此,我们就可以重新排序,按照 (x,y,z) 为一二三关键字来排序,由于修改并不会很多,所以时间复杂度是能接受的。值得一提的是,由于我们从二维上升到了三维,我们的块长也应重构为 \(\sqrt[3]{n^2}\)。
Code:
#include<bits/stdc++.h>
const int N=1.4e5;
const int M=1e6+5;
using namespace std;
int cnt[M],blc[N],ans[N],a[N];
struct query{
int l,r,tim,id;
bool operator <(const query &q)const{
return blc[l]==blc[q.l] ? (blc[r]==blc[q.r] ? tim<q.tim :r<q.r) : l<q.l;
}
}q[N];
struct replz{
int x,y;
}re[N];
int n,m,tmp,S,tot_q,tot_r;
inline void add(int x){tmp+=(++cnt[a[x]]==1);}
inline void del(int x){tmp-=(--cnt[a[x]]==0);}
inline void change(int i,int tim)
{
int x=re[tim].x,&y=re[tim].y;
if(q[i].l<=x&&x<=q[i].r)
{
del(x);
swap(a[x],y);
add(x);
}
else swap(a[x],y);
}
char c[3];
void work()
{
cin>>n>>m;S=pow(n,0.666);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);blc[i]=i/S;
}
for(int i=1,x,y;i<=m;i++)
{
scanf("%s",c);scanf("%d%d",&x,&y);
if(c[0]=='Q')q[++tot_q]={x,y,tot_r,tot_q};
else re[++tot_r]={x,y};
}
sort(q+1,q+1+tot_q);
int l=1,r=0,tim=0;
for(int i=1;i<=tot_q;i++)
{
while(q[i].l<l)add(--l);
while(l<q[i].l)del(l++);
while(q[i].r<r)del(r--);
while(r<q[i].r)add(++r);
while(tim<q[i].tim)change(i,++tim);
while(q[i].tim<tim)change(i,tim--);
ans[q[i].id]=tmp;
}
for(int i=1;i<=tot_q;i++)printf("%d\n",ans[i]);
}
int main()
{
//freopen("P1903.in","r",stdin);
//freopen("P1903.out","w",stdout);
work();
return 0;
}

浙公网安备 33010602011771号