带修莫队

P1903 [国家集训队]数颜色 / 维护队列

题目描述

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

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

2、 R P Col 把第P支画笔替换为颜色Col。

为了满足墨墨的要求,你知道你需要干什么了吗?

输入输出格式

输入格式:

 

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

 

输出格式:

 

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

 

输入输出样例

输入样例#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

说明

对于100%的数据,N≤50000,M≤50000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

本题可能轻微卡常数

 

带修莫队和普通莫队的区别在于多加了一个时间 多了俩个时间的while

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
int  Be[maxn],col[maxn],x,y,n,m,color[maxn*100],Ans[maxn],ans,l=1,r,T,unit,t,Time,now[maxn];
char sign;
struct node
{
	int l,r,Tim,ID,sum;
} q[maxn];
struct aa
{
	int pos,New,Old;
} c[maxn];
bool cmp(node a,node b)
{
	return Be[a.l]==Be[b.l]?(Be[a.r]==Be[b.r]?a.Tim<b.Tim:a.r<b.r):a.l<b.l;
}//分块排序
bool Cmp(node x,node y)
{
	return x.ID<y.ID;
}//按id大小排序 准备输出
void revise(int x,int d)
{
	if(color[x]!=0) ans--;//只有在这个颜色有的时候才消去一下影响
	color[x]+=d;
	if(color[x]!=0) ans++;//只有在这个颜色有的时候才加上影响
}
void gogoing(int x,int d)
{
	if(l<=x&&x<=r)//如果更新时间的时候这个点在要求区间中
	{
		revise(d,1);//加上d 
		revise(col[x],-1);//消去col[x];
	}
	col[x]=d;//更改
}
int main()
{
	scanf("%d%d",&n,&m);
	unit=pow(n,0.666666);//最优分块方法
	for(int i=1; i<=n; i++)
	{
		scanf("%d",&col[i]);
		Be[i]=i/unit+1;
		now[i]=col[i];
	}
	for(int i=1; i<=m; i++)
	{
		scanf(" %c %d%d",&sign,&x,&y);
		if(sign=='Q')
		{
			q[++t]=(node)
			{
				x,y,Time,t
			};//存数
		}
		if(sign=='R')
		{
			c[++Time]=(aa)
			{
				x,y,now[x]
			};
			now[x]=y;
		}//存数
	}
	sort(q+1,q+t+1,cmp);
	for(int i=1; i<=t; i++)
	{
		//按时间和左右边界进行美丽的暴力
		while(T<q[i].Tim) gogoing(c[T+1].pos,c[T+1].New),T++;
		while(T>q[i].Tim) gogoing(c[T].pos,c[T].Old),T--;
		while(l<q[i].l)revise(col[l],-1),l++;
		while(l>q[i].l)revise(col[l-1],1),l--;
		while(r<q[i].r)revise(col[r+1],1),r++;
		while(r>q[i].r)revise(col[r],-1),r--;
		q[i].sum=ans;//存答案
	}
	sort(q+1,q+t+1,Cmp);
	for(int i=1; i<=t; i++)
	{
		printf("%d\n",q[i].sum);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值