洛谷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;
}

 

posted @ 2020-03-07 22:21  xjqxjq  阅读(136)  评论(0编辑  收藏  举报