(带修莫队)洛谷 P1903 数颜色 题解

这是一篇很简略的题解,只是用来提醒自己待修莫队怎么写,部分摘录了其他人的博客,代码也是好久之前写的

题意

墨墨购买了一套 N N N 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

  • Q   L   R Q\ L\ R Q L R 代表询问你从第 L L L 支画笔到第 R R R 支画笔中共有几种不同颜色的画笔。

  • R   P   C R\ P\ C R P C 把第 P P P 支画笔替换为颜色 C C C

对于每一个 Q Q Q 的询问,你需要在对应的行中给出一个数字,代表第 L L L 支画笔到第 R R R 支画笔中共有几种不同颜色的画笔。

n , m ≤ 133333 n,m \leq 133333 n,m133333

思路

这个区间的 count,肯定想用莫队维护区间的桶,询问就是这样。但是还有个修改操作。因此需要使用带修莫队

带修莫队的原理,转载自子相詹的这篇博客

对询问加入时间戳 t t t(即该操作在第 t t t 个修改操作之后),并添加一个时间指针,在移动指针时优先移动时间指针。
排序方式转变为:以左端点所在块为第一关键字,右端点所在块为第二关键字,时间戳为第三关键字进行排序 块长定义为 n 2 3 n^{\frac{2}{3}} n32,因此总复杂度复杂度为 Θ ( n 5 3 ) \Theta(n^{\frac{5}{3}}) Θ(n35) 级别。
普通莫队由于没有修改,因而时间戳均为 0 0 0,所以可以将普通莫队看作带修莫队的一个特殊情况。这也就是说,只有两个询问处于同一个时间点,就能放心大胆地移动指向询问的指针。在移动完双指针后,上一个询问与当前询问便会处于同一个时间点内,即两个询问之间没有修改,于是问题被转化成了普通莫队。

我们按图索骥,给询问搞上时间戳然后进行排序。另开一个指针搞修改。

同时注意审题,是第 p p p 支换乘颜色为 x x x 的。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{
   ll s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
inline void write(ll x)
{ 
    if(x==0){putchar('0');return;}
	ll len=0,k1=x,c[10005];
	if(k1<0)k1=-k1,putchar('-');
	while(k1)c[len++]=k1%10+'0',k1/=10;
	while(len--)putchar(c[len]);
}
const ll N=1e6+9;
ll n,m,a[N],l,r;
ll bitSize,res,b[N],cnt[N],ans[N];
ll nq;
//char op;
struct query
{
	ll l,r,t,id;
}q[N];
bool cmp(const query &x,const query &y)
{
	return b[x.l]==b[y.l]?(b[x.r]==b[y.r]?x.t<y.t:b[x.r]<b[y.r]):b[x.l]<b[y.l];
}
ll ti;
struct modify
{
	ll p,x;
}modi[N];
void add(ll x)
{
	cnt[x]++;
	if(cnt[x]==1)res++;
}
void del(ll x)
{
	cnt[x]--;
	if(cnt[x]==0)res--;
}
void M(ll t,ll i)
{
	if(q[i].l<=modi[t].p&&modi[t].p<=q[i].r)//第p支->x
	{
		add(modi[t].x);
		del(a[modi[t].p]);
	}
	swap(modi[t].x,a[modi[t].p]);
}
int main()
{
	n=read(),m=read();
	bitSize=pow(n,0.667);//n^(2/3)
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		b[i]=(i-1)/bitSize+1;
	}
	for(int i=1;i<=m;i++)
	{
	//	cin>>op;
		char op[3];
		scanf("%s",&op);
		l=read(),r=read();
		if(op[0]=='Q')q[++nq]=(query){l,r,ti,nq};
		else modi[++ti]=(modify){l,r};
	}
	sort(q+1,q+nq+1,cmp);
	ll l=1,r=0,t=0;
	for(int i=1;i<=nq;i++)
	{
		while(l<q[i].l)del(l++);
		while(r>q[i].r)del(r--);
		while(l>q[i].l)add(--l);
		while(r<q[i].r)add(++r);
		while(t<q[i].t)M(++t,i);
		while(t>q[i].t)M(t--,i);
		ans[q[i].id]=res;
	}
	for(int i=1;i<=nq;i++)
	write(ans[i]),puts("");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值