[国家集训队]数颜色

题目:洛谷P1903、BZOJ2120。

题目大意:
给你一列数,有两种操作:

1. 询问区间$[L, R]$内不同数出现的个数。
2. 单点修改。

解题思路:
带修莫队。
仍然运用分块思路,加了一个修改时间而已。
所以以$L$所在的块为第一关键字,$R$所在的块为第二关键字,修改时间为第三关键字排序即可。

C++ Code:

#include<bits/stdc++.h>
int n,m,sz,a[10005],cnt[1000005],k=0,T=0,b[10005],ans,out[10005];
char c[3];
#define be(i) (i/sz)
inline int readint(){
	int c=getchar();
	for(;!isdigit(c);c=getchar());
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return d;
}
struct query{
	int l,r,t,num;
	bool operator <(const query& rhs)const{
		if(be(l)!=be(rhs.l))return l<rhs.l;
		if(be(r)!=be(rhs.r))return r<rhs.r;
		return t<rhs.t;
	}
}q[10005];
struct Time{
	int pos,now,old;
}tim[10005];
inline void change(int d,int c){
	cnt[d]+=c;
	if(c==-1&&!cnt[d])--ans;else
	if(c==1&&cnt[d]==1)++ans;
}
int main(){
	n=readint(),m=readint();
	sz=(int)pow(n,2.0/3);
	for(int i=1;i<=n;++i)a[i]=b[i]=readint();
	for(int i=1;i<=m;++i){
		scanf("%s",c);
		if(c[0]=='Q'){
			q[++k].l=readint();
			q[k].r=readint();
			q[k].t=T;
			q[k].num=k;
		}else{
			tim[++T].pos=readint();
			tim[T].now=readint();
			tim[T].old=b[tim[T].pos];
			b[tim[T].pos]=tim[T].now;
		}
	}
	std::sort(q+1,q+k+1);
	memset(cnt,0,sizeof cnt);
	int l=1,r=1,t=0;
	ans=1;
	++cnt[a[1]];
	for(int i=1;i<=k;++i){
		while(t<q[i].t){
			++t;
			if(l<=tim[t].pos&&tim[t].pos<=r)
			change(a[tim[t].pos],-1),change(tim[t].now,1);
			a[tim[t].pos]=tim[t].now;
		}
		while(q[i].t<t){
			if(l<=tim[t].pos&&tim[t].pos<=r)
			change(a[tim[t].pos],-1),change(tim[t].old,1);
			a[tim[t].pos]=tim[t].old;
			--t;
		}
		while(r<q[i].r)change(a[++r],1);
		while(q[i].r<r)change(a[r--],-1);
		while(q[i].l<l)change(a[--l],1);
		while(l<q[i].l)change(a[l++],-1);
		out[q[i].num]=ans;
	}
	for(int i=1;i<=k;++i)printf("%d\n",out[i]);
	return 0;
}

 

posted @ 2018-04-01 20:19  Mrsrz  阅读(320)  评论(0编辑  收藏  举报