题解 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;
}
posted @ 2021-10-25 21:05  Farkas_W  阅读(53)  评论(0编辑  收藏  举报