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