题解 SP30906 【ADAUNIQ - Ada and Unique Vegetable】
\[\text{题目大意}
\]
\(\quad\)你需要维护一个长度为 \(n\) 的序列,支持两种操作。
- 第一种操作为 \(1\) \(x\) \(y\),代表将第 \(x\) 个数修改为 \(y\)。
- 第二种操作为 \(2\) \(l\) \(r\),需要你求出在第 \(l\) 到 \(r\) 个数中恰好出现了一次的数的数量。
- \(n,a_i≤2 \times10^5\)。
- 本题的下标从 \(0\) 开始。(WA了一次就是这个原因)
\[\text{思路}
\]
\(\quad\)本题就是带修莫队板子题,没有学过的可以先做做P1903 [国家集训队]数颜色 / 维护队列。
\(\quad\)大体思路就是先存储询问和修改操作,分开来存,并记录时间,之后将询问排序。
- 以询问左边界 \(l\) 为第一关键字,按块的序号排序。
- 以询问右边界 \(r\) 为第二关键字,按块的序号排序。
- 以时间 \(t\) 为第三关键字,按先后排序。
\(\quad\)之后依次处理询问,记录答案即可。
\(\quad\)值得一提的是块的大小一般是 \(n^{ \frac{2}{3}}\),这样时间复杂度理论上最简,为 。
\(\quad\)还有什么不懂的就看看代码吧!
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define re register int
#define il inline
using namespace std;
il int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
il void print(int x)
{
if(x<0)putchar('-'),x=-x;
if(x/10)print(x/10);
putchar(x%10+'0');
}
const int N=2e5+5;
int n,m,blo,num,t1,t2,a[N],p[N],ans[N];
struct node{
int l,r,id,tim;
}q[N];
struct nod{
int id,c1,c2;
}c[N];
il bool cmp(node a,node b){
if(a.l/blo!=b.l/blo)return a.l/blo<b.l/blo;//以询问左边界l为第一关键字,按块的序号排序。
if(a.r/blo!=b.r/blo)return a.r/blo<b.r/blo;//以询问右边界r为第二关键字,按块的序号排序。
return a.tim<b.tim; //以时间t为第三关键字,按先后排序。
}
il void add(int x)
{
if(p[x]==1)num--;
p[x]++;
if(p[x]==1)num++;
}
il void del(int x)
{
if(p[x]==1)num--;
p[x]--;
if(p[x]==1)num++;
}
signed main()
{
n=read();m=read();blo=pow(n,0.666);//blo表示块长
for(re i=1;i<=n;i++)a[i]=read();
for(re i=1;i<=m;i++)
{
int op=read();
if(op==2){
q[++t1].l=read()+1;q[t1].r=read()+1;//题目所给的下标从0开始
q[t1].id=t1;q[t1].tim=t2;//id用来保存答案,tim为时间轴
}
else {
c[++t2].id=read()+1;//题目所给的下标从0开始,id记录修改的位置
c[t2].c1=a[c[t2].id];//记录之前的数字,方便撤销
a[c[t2].id]=c[t2].c2=read();//修改操作
}
}
sort(q+1,q+t1+1,cmp);
int l=1,r=0,t=t2;//因为前面输入的时候把修改操作都做了一遍,所以此时时间t=t2
for(re i=1;i<=t1;i++)
{
//依次调整l,r
while(l<q[i].l)del(a[l++]);
while(l>q[i].l)add(a[--l]);
while(r<q[i].r)add(a[++r]);
while(r>q[i].r)del(a[r--]);
while(t<q[i].tim){//修改
int x=c[++t].id;
if(x>=l&&x<=r)del(a[x]);//只有x符合范围才有可能修改答案
a[x]=c[t].c2;
if(x>=l&&x<=r)add(a[x]);
}
while(t>q[i].tim){//撤销
int x=c[t].id;
if(x>=l&&x<=r)del(a[x]);
a[x]=c[t--].c1;
if(x>=l&&x<=r)add(a[x]);
}
ans[q[i].id]=num;
}
for(re i=1;i<=t1;i++)print(ans[i]),putchar('\n');
return 0;
}