题目描述
题解
第一眼看过去是带修莫队。效率 O ( n 5 3 ) O(n^{\frac{5}{3}}) O(n35) 。可惜洛谷给出的标签是树套树,于是思考一下怎么用数据结构维护。
如果没有修改的话,我们考虑怎么在线计算答案,考虑到一种颜色只能计算一次,所以可以想到如果 [ l , r ] [l,r] [l,r] 中有的颜色的前驱出现在 [ 0 , l − 1 ] [0,l-1] [0,l−1] (没有即 0 0 0 ),那就造成贡献,因此我们可以用主席树维护 [ 1 , r ] [1,r] [1,r] 中每个位置的前驱对应位置的个数。统计答案的时候就统计 [ l , r ] [l,r] [l,r] 中 [ 0 , l − 1 ] [0,l-1] [0,l−1] 的标记数即可。
考虑修改,那我们就用 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;
}