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

题目描述
题解

第一眼看过去是带修莫队。效率 O ( n 5 3 ) O(n^{\frac{5}{3}}) O(n35) 。可惜洛谷给出的标签是树套树,于是思考一下怎么用数据结构维护。

如果没有修改的话,我们考虑怎么在线计算答案,考虑到一种颜色只能计算一次,所以可以想到如果 [ l , r ] [l,r] [l,r] 中有的颜色的前驱出现在 [ 0 , l − 1 ] [0,l-1] [0,l1] (没有即 0 0 0 ),那就造成贡献,因此我们可以用主席树维护 [ 1 , r ] [1,r] [1,r] 中每个位置的前驱对应位置的个数。统计答案的时候就统计 [ l , r ] [l,r] [l,r] [ 0 , l − 1 ] [0,l-1] [0,l1] 的标记数即可。

考虑修改,那我们就用 set \text{set} set 维护前驱,然后套树状数组即可。效率 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=N*200;
int n,m,t,ls[M],T[N],rs[M],s[M],a[N];
set<int>st[1000005];
set<int>::iterator it;char ch[3];
#define mid ((l+r)>>1)
void upd(int& x,int l,int r,int p,int v){
	if (!x) x=++t;s[x]+=v;
	if (l==r) return;
	if (mid>=p) upd(ls[x],l,mid,p,v);
	else upd(rs[x],mid+1,r,p,v);
}
void upd(int x,int y,int v){
	for (;x<=n;x+=x&-x) upd(T[x],0,n,y,v);
}
int qry(int x,int l,int r,int R){
	if (!x) return 0;
	if (r<=R) return s[x];
	if (mid>=R) return qry(ls[x],l,mid,R);
	return s[ls[x]]+qry(rs[x],mid+1,r,R);
}
int qry(int l,int r,int p){
	int v=0;
	for (;r;r-=r&-r) v+=qry(T[r],0,n,p);
	for (;l;l-=l&-l) v-=qry(T[l],0,n,p);
	return v;
}
int main(){
	cin>>n>>m;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),st[a[i]].insert(i);
	for (int i=1000000;i;i--) st[i].insert(0);
	for (int i=1;i<=n;i++)
		it=st[a[i]].lower_bound(i),it--,upd(i,*it,1);
	for (int u,v,l,r,i=1;i<=m;i++){
		scanf("%s%d%d",ch,&u,&v);
		if (ch[0]=='Q') printf("%d\n",qry(u-1,v,u-1));
		else{
			st[a[u]].erase(st[a[u]].find(u));
			it=st[a[u]].lower_bound(u);l=r=0;
			if (it!=st[a[u]].end()) r=*it;
			it--;l=*it;upd(u,l,-1);
			if (r) upd(r,u,-1),upd(r,l,1);
			it=st[v].lower_bound(u);l=r=0;
			if (it!=st[v].end()) r=*it;
			it--;l=*it;upd(u,l,1);
			if (r) upd(r,l,-1),upd(r,u,1);
			st[v].insert(u);a[u]=v;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值